removes federated CLI launch functionality

pull/3030/head
Kieran Prasch 2022-12-07 11:12:07 +00:00
parent c149411fb6
commit 4c93f5d859
6 changed files with 200 additions and 167 deletions

View File

@ -12,12 +12,12 @@ from nucypher.cli.literature import (
COLLECT_NUCYPHER_PASSWORD,
DECRYPTING_CHARACTER_KEYSTORE,
GENERIC_PASSWORD_PROMPT,
PASSWORD_COLLECTION_NOTICE
PASSWORD_COLLECTION_NOTICE,
)
from nucypher.config.base import CharacterConfiguration
from nucypher.config.constants import NUCYPHER_ENVVAR_KEYSTORE_PASSWORD
from nucypher.crypto.keystore import _WORD_COUNT, Keystore
from nucypher.utilities.emitters import StdoutEmitter
from nucypher.crypto.keystore import Keystore, _WORD_COUNT
def get_password_from_prompt(prompt: str = GENERIC_PASSWORD_PROMPT, envvar: str = None, confirm: bool = False) -> str:
@ -48,9 +48,9 @@ def unlock_signer_account(config: CharacterConfiguration, json_ipc: bool) -> Non
else:
account = config.checksum_address
eth_password_is_needed = all((not config.federated_only,
not config.signer.is_device(account=account),
not config.dev_mode))
eth_password_is_needed = all(
(not config.signer.is_device(account=account), not config.dev_mode)
)
__password = None
if eth_password_is_needed:

View File

@ -1,26 +1,36 @@
from pathlib import Path
import click
from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.cli.actions.auth import get_client_password, get_nucypher_password, recover_keystore
from nucypher.cli.actions.configure import (
destroy_configuration,
handle_missing_configuration_file,
get_or_update_configuration,
collect_operator_ip_address
from nucypher.cli.actions.auth import (
get_client_password,
get_nucypher_password,
recover_keystore,
)
from nucypher.cli.actions.configure import (
collect_operator_ip_address,
destroy_configuration,
)
from nucypher.cli.actions.configure import forget as forget_nodes
from nucypher.cli.actions.configure import (
get_or_update_configuration,
handle_missing_configuration_file,
perform_startup_ip_check,
)
from nucypher.cli.actions.select import (
select_client_account,
select_config_file,
select_network,
)
from nucypher.cli.actions.configure import forget as forget_nodes, perform_startup_ip_check
from nucypher.cli.actions.select import select_client_account, select_config_file, select_network
from nucypher.cli.commands.deploy import option_gas_strategy
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import (
DEVELOPMENT_MODE_WARNING,
FORCE_MODE_WARNING,
SUCCESSFUL_MANUALLY_SAVE_METADATA
SELECT_OPERATOR_ACCOUNT,
SELECT_PAYMENT_NETWORK,
SUCCESSFUL_MANUALLY_SAVE_METADATA,
)
from nucypher.cli.options import (
group_options,
@ -28,23 +38,22 @@ from nucypher.cli.options import (
option_config_root,
option_dev,
option_dry_run,
option_federated_only,
option_eth_provider_uri,
option_force,
option_key_material,
option_light,
option_lonely,
option_max_gas_price,
option_min_stake,
option_network,
option_payment_method,
option_payment_network,
option_payment_provider,
option_poa,
option_eth_provider_uri,
option_policy_registry_filepath,
option_registry_filepath,
option_signer_uri,
option_teacher_uri,
option_lonely,
option_max_gas_price,
option_key_material,
option_payment_provider,
option_payment_method,
option_payment_network,
option_policy_registry_filepath
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, NETWORK_PORT, OPERATOR_IP
@ -52,47 +61,40 @@ from nucypher.cli.utils import make_cli_character, setup_emitter
from nucypher.config.characters import UrsulaConfiguration
from nucypher.config.constants import (
NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD,
TEMPORARY_DOMAIN
TEMPORARY_DOMAIN,
)
from nucypher.crypto.keystore import Keystore
class UrsulaConfigOptions:
__option_name__ = 'config_options'
__option_name__ = "config_options"
def __init__(self,
eth_provider_uri: str,
operator_address: str,
federated_only: bool,
rest_host: str,
rest_port: int,
network: str,
registry_filepath: Path,
policy_registry_filepath: Path,
dev: bool,
poa: bool,
light: bool,
gas_strategy: str,
max_gas_price: int, # gwei
signer_uri: str,
availability_check: bool,
lonely: bool,
payment_method: str,
payment_provider: str,
payment_network: str
):
if federated_only:
if registry_filepath or policy_registry_filepath:
raise click.BadOptionUsage(option_name="--registry-filepath",
message=click.style("--registry-filepath and --policy-registry-filepath cannot be used in federated mode.", fg="red"))
def __init__(
self,
eth_provider_uri: str,
operator_address: str,
rest_host: str,
rest_port: int,
network: str,
registry_filepath: Path,
policy_registry_filepath: Path,
dev: bool,
poa: bool,
light: bool,
gas_strategy: str,
max_gas_price: int, # gwei
signer_uri: str,
availability_check: bool,
lonely: bool,
payment_method: str,
payment_provider: str,
payment_network: str,
):
self.eth_provider_uri = eth_provider_uri
self.signer_uri = signer_uri
self.operator_address = operator_address
self.federated_only = federated_only
self.rest_host = rest_host
self.rest_port = rest_port # FIXME: not used in generate()
self.domain = network
@ -123,14 +125,13 @@ class UrsulaConfigOptions:
signer_uri=self.signer_uri,
gas_strategy=self.gas_strategy,
max_gas_price=self.max_gas_price,
checksum_address=self.operator_address,
federated_only=self.federated_only,
operator_address=self.operator_address,
rest_host=self.rest_host,
rest_port=self.rest_port,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network
payment_network=self.payment_network,
)
else:
if not config_file:
@ -152,11 +153,10 @@ class UrsulaConfigOptions:
rest_port=self.rest_port,
poa=self.poa,
light=self.light,
federated_only=self.federated_only,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network
payment_network=self.payment_network,
)
except FileNotFoundError:
return handle_missing_configuration_file(character_config_class=UrsulaConfiguration, config_file=config_file)
@ -168,60 +168,64 @@ class UrsulaConfigOptions:
def generate_config(self, emitter, config_root, force, key_material):
if self.dev:
raise RuntimeError('Persistent configurations cannot be created in development mode.')
raise RuntimeError(
"Persistent configurations cannot be created in development mode."
)
if (not self.operator_address) and not self.federated_only:
prompt = "Select operator account"
self.operator_address = select_client_account(emitter=emitter,
prompt=prompt,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri)
if not self.operator_address:
prompt = SELECT_OPERATOR_ACCOUNT
self.operator_address = select_client_account(
emitter=emitter,
prompt=prompt,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri,
)
# Resolve rest host
if not self.rest_host:
self.rest_host = collect_operator_ip_address(emitter, network=self.domain, force=force)
return UrsulaConfiguration.generate(password=get_nucypher_password(emitter=emitter, confirm=True),
key_material=bytes.fromhex(key_material) if key_material else None,
config_root=config_root,
rest_host=self.rest_host,
rest_port=self.rest_port,
domain=self.domain,
federated_only=self.federated_only,
operator_address=self.operator_address,
registry_filepath=self.registry_filepath,
policy_registry_filepath=self.policy_registry_filepath,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri,
gas_strategy=self.gas_strategy,
max_gas_price=self.max_gas_price,
poa=self.poa,
light=self.light,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network
)
return UrsulaConfiguration.generate(
password=get_nucypher_password(emitter=emitter, confirm=True),
key_material=bytes.fromhex(key_material) if key_material else None,
config_root=config_root,
rest_host=self.rest_host,
rest_port=self.rest_port,
domain=self.domain,
operator_address=self.operator_address,
registry_filepath=self.registry_filepath,
policy_registry_filepath=self.policy_registry_filepath,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri,
gas_strategy=self.gas_strategy,
max_gas_price=self.max_gas_price,
poa=self.poa,
light=self.light,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network,
)
def get_updates(self) -> dict:
payload = dict(rest_host=self.rest_host,
rest_port=self.rest_port,
domain=self.domain,
federated_only=self.federated_only,
operator_address=self.operator_address,
registry_filepath=self.registry_filepath,
policy_registry_filepath=self.policy_registry_filepath,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri,
gas_strategy=self.gas_strategy,
max_gas_price=self.max_gas_price,
poa=self.poa,
light=self.light,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network
)
payload = dict(
rest_host=self.rest_host,
rest_port=self.rest_port,
domain=self.domain,
operator_address=self.operator_address,
registry_filepath=self.registry_filepath,
policy_registry_filepath=self.policy_registry_filepath,
eth_provider_uri=self.eth_provider_uri,
signer_uri=self.signer_uri,
gas_strategy=self.gas_strategy,
max_gas_price=self.max_gas_price,
poa=self.poa,
light=self.light,
availability_check=self.availability_check,
payment_method=self.payment_method,
payment_provider=self.payment_provider,
payment_network=self.payment_network,
)
# Depends on defaults being set on Configuration classes, filtrates None values
updates = {k: v for k, v in payload.items() if v is not None}
return updates
@ -234,10 +238,21 @@ group_config_options = group_options(
signer_uri=option_signer_uri,
gas_strategy=option_gas_strategy,
max_gas_price=option_max_gas_price,
operator_address=click.option('--operator-address', help="Run with the specified operator address", type=EIP55_CHECKSUM_ADDRESS),
federated_only=option_federated_only,
rest_host=click.option('--rest-host', help="The host IP address to run Ursula network services on", type=OPERATOR_IP),
rest_port=click.option('--rest-port', help="The host port to run Ursula network services on", type=NETWORK_PORT),
operator_address=click.option(
"--operator-address",
help="Run with the specified operator address",
type=EIP55_CHECKSUM_ADDRESS,
),
rest_host=click.option(
"--rest-host",
help="The host IP address to run Ursula network services on",
type=OPERATOR_IP,
),
rest_port=click.option(
"--rest-port",
help="The host port to run Ursula network services on",
type=NETWORK_PORT,
),
network=option_network(),
registry_filepath=option_registry_filepath,
policy_registry_filepath=option_policy_registry_filepath,
@ -263,13 +278,13 @@ class UrsulaCharacterOptions:
def create_character(self, emitter, config_file, json_ipc, load_seednodes=True):
ursula_config = self.config_options.create_config(emitter, config_file)
password_required = all((not ursula_config.federated_only,
not self.config_options.dev,
not json_ipc))
password_required = all((not self.config_options.dev, not json_ipc))
__password = None
if password_required:
__password = get_client_password(checksum_address=ursula_config.operator_address,
envvar=NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD)
__password = get_client_password(
checksum_address=ursula_config.operator_address,
envvar=NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD,
)
try:
URSULA = make_cli_character(character_config=ursula_config,
@ -315,20 +330,39 @@ def init(general_config, config_options, force, config_root, key_material):
_pre_launch_warnings(emitter, dev=None, force=force)
if not config_root:
config_root = general_config.config_root
if not config_options.federated_only and not config_options.eth_provider_uri:
raise click.BadOptionUsage('--eth-provider', message=click.style("--eth-provider is required to initialize a new ursula.", fg="red"))
if not config_options.federated_only and not config_options.payment_provider:
raise click.BadOptionUsage('--payment-provider', message=click.style("--payment-provider is required to initialize a new ursula.", fg="red"))
if not config_options.federated_only and not config_options.domain:
config_options.domain = select_network(emitter, message="Select Staking Network", network_type=NetworksInventory.ETH)
if not config_options.federated_only and not config_options.payment_network:
config_options.payment_network = select_network(emitter, message="Select Payment Network", network_type=NetworksInventory.POLYGON)
ursula_config = config_options.generate_config(emitter=emitter,
config_root=config_root,
force=force,
key_material=key_material)
if not config_options.eth_provider_uri:
raise click.BadOptionUsage(
"--eth-provider",
message=click.style(
"--eth-provider is required to initialize a new ursula.", fg="red"
),
)
if not config_options.payment_provider:
raise click.BadOptionUsage(
"--payment-provider",
message=click.style(
"--payment-provider is required to initialize a new ursula.", fg="red"
),
)
if not config_options.domain:
config_options.domain = select_network(
emitter,
message="Select Staking Network",
network_type=NetworksInventory.ETH,
)
if not config_options.payment_network:
config_options.payment_network = select_network(
emitter,
message=SELECT_PAYMENT_NETWORK,
network_type=NetworksInventory.POLYGON,
)
ursula_config = config_options.generate_config(
emitter=emitter, config_root=config_root, force=force, key_material=key_material
)
filepath = ursula_config.to_configuration_file()
paint_new_installation_help(emitter, new_configuration=ursula_config, filepath=filepath)
paint_new_installation_help(
emitter, new_configuration=ursula_config, filepath=filepath
)
@ursula.command()
@ -393,18 +427,21 @@ def run(general_config, character_options, config_file, dry_run, prometheus, met
_pre_launch_warnings(emitter, dev=dev_mode, force=None)
prometheus_config: 'PrometheusMetricsConfig' = None
prometheus_config: "PrometheusMetricsConfig" = None
if prometheus and not dev_mode:
# Locally scoped to prevent import without prometheus explicitly installed
from nucypher.utilities.prometheus.metrics import PrometheusMetricsConfig
prometheus_config = PrometheusMetricsConfig(port=metrics_port,
metrics_prefix=metrics_prefix,
listen_address=metrics_listen_address,
collection_interval=metrics_interval)
ursula_config, URSULA = character_options.create_character(emitter=emitter,
config_file=config_file,
json_ipc=general_config.json_ipc)
prometheus_config = PrometheusMetricsConfig(
port=metrics_port,
metrics_prefix=metrics_prefix,
listen_address=metrics_listen_address,
collection_interval=metrics_interval,
)
ursula_config, URSULA = character_options.create_character(
emitter=emitter, config_file=config_file, json_ipc=general_config.json_ipc
)
if ip_checkup and not (dev_mode or lonely):
# Always skip startup IP checks for dev and lonely modes.

View File

@ -24,8 +24,6 @@ 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"
PERIOD_ADVANCED_WARNING = "Current period advanced before the action could be completed. Please try again."
#
@ -92,6 +90,8 @@ nucypher {init_command}
SELECT_NETWORK = "Select Network"
SELECT_PAYMENT_NETWORK = "Select Payment Network"
NO_CONFIGURATIONS_ON_DISK = "No {name} configurations found. Run 'nucypher {command} init' then try again."
SUCCESSFUL_UPDATE_CONFIGURATION_VALUES = "Updated configuration values: {fields}"
@ -141,13 +141,18 @@ GENERIC_PASSWORD_PROMPT = "Enter password"
DECRYPTING_CHARACTER_KEYSTORE = 'Authenticating {name}'
REPEAT_FOR_CONFIRMATION = "Repeat for confirmation:"
#
# Networking
#
CONFIRM_IPV4_ADDRESS_QUESTION = "Is this the public-facing address of Ursula?"
CONFIRM_URSULA_IPV4_ADDRESS = "Detected IPv4 address ({rest_host}) - Is this the public-facing address of Ursula?"
CONFIRM_URSULA_IPV4_ADDRESS = (
"Detected IPv4 address ({rest_host}) - " + CONFIRM_IPV4_ADDRESS_QUESTION
)
COLLECT_URSULA_IPV4_ADDRESS = "Enter Ursula's public-facing IPv4 address"
@ -250,6 +255,8 @@ SUCCESSFUL_MANUALLY_SAVE_METADATA = "Successfully saved node metadata to {metada
STAKING_PROVIDER_UNAUTHORIZED = '{provider} is not authorized.'
SELECT_OPERATOR_ACCOUNT = "Select operator account"
CONFIRM_BONDING = 'Are you sure you want to bond staking provider {provider} to operator {operator}?'
BONDING_TIME = 'Bonding/Unbonding not permitted until {date}.'

View File

@ -13,11 +13,12 @@ from nucypher.cli.types import (
EIP55_CHECKSUM_ADDRESS,
EXISTING_READABLE_FILE,
GWEI,
MIN_AUTHORIZATION,
NETWORK_PORT,
NuCypherNetworkName,
WEI,
PAYMENT_METHOD_CHOICES,
STAKED_TOKENS_RANGE,
MIN_AUTHORIZATION, PAYMENT_METHOD_CHOICES
WEI,
NuCypherNetworkName,
)
from nucypher.utilities.logging import Logger
@ -29,7 +30,6 @@ option_dev = click.option('--dev', '-d', help="Enable development mode", is_flag
option_dry_run = click.option('--dry-run', '-x', help="Execute normally without actually starting the node", is_flag=True)
option_etherscan = click.option('--etherscan/--no-etherscan', help="Enable/disable viewing TX in Etherscan")
option_event_name = click.option('--event-name', help="Specify an event by name", type=click.STRING)
option_federated_only = click.option('--federated-only/--decentralized', '-F', help="Connect only to federated nodes", is_flag=True, default=None)
option_force = click.option('--force', help="Don't ask for confirmation", is_flag=True)
option_gas_strategy = click.option('--gas-strategy', help="Operate with a specified gas price strategy", type=click.STRING) # TODO: GAS_STRATEGY_CHOICES
option_key_material = click.option('--key-material', help="A pre-secured hex-encoded secret to use for private key derivations", type=click.STRING)

View File

@ -32,16 +32,16 @@ def paint_node_status(emitter, ursula, start_time):
'Fleet State.......... {}'.format(fleet_state),
'Learning Status ..... {}'.format(learning_status),
'Learning Round ...... Round #{}'.format(ursula._learning_round),
'Operating Mode ...... {}'.format('Federated' if ursula.federated_only else 'Decentralized'),
'Rest Interface ...... {}'.format(ursula.rest_url()),
'Node Storage Type ... {}'.format(ursula.node_storage._name.capitalize()),
'Known Nodes ......... {}'.format(len(ursula.known_nodes)),
teacher]
if not ursula.federated_only:
operator_address = 'Operator Address ...... {}'.format(ursula.operator_address)
current_period = f'Current Period ...... {ursula.application_agent.get_current_period()}'
stats.extend([current_period, operator_address])
operator_address = "Operator Address ...... {}".format(ursula.operator_address)
current_period = (
f"Current Period ...... {ursula.application_agent.get_current_period()}"
)
stats.extend([current_period, operator_address])
if ursula._availability_tracker:
if ursula._availability_tracker.running:
@ -57,13 +57,8 @@ def paint_node_status(emitter, ursula, start_time):
def paint_known_nodes(emitter, ursula) -> None:
# Gather Data
known_nodes = ursula.known_nodes
number_of_known_nodes = len(ursula.node_storage.all(federated_only=ursula.federated_only))
seen_nodes = len(ursula.node_storage.all(federated_only=ursula.federated_only, certificates_only=True))
# Operating Mode
federated_only = ursula.federated_only
if federated_only:
emitter.echo("Configured in Federated Only mode", color='green')
number_of_known_nodes = len(ursula.node_storage.all())
seen_nodes = len(ursula.node_storage.all(certificates_only=True))
# Heading
label = "Known Nodes (connected {} / seen {})".format(number_of_known_nodes, seen_nodes)

View File

@ -14,31 +14,30 @@ from nucypher.blockchain.eth.events import EventRecord
from nucypher.blockchain.eth.interfaces import (
BlockchainDeployerInterface,
BlockchainInterface,
BlockchainInterfaceFactory
BlockchainInterfaceFactory,
)
from nucypher.blockchain.eth.registry import (
BaseContractRegistry,
InMemoryContractRegistry,
LocalContractRegistry
LocalContractRegistry,
)
from nucypher.characters.base import Character
from nucypher.utilities.emitters import StdoutEmitter
from nucypher.cli.actions.auth import (
get_nucypher_password,
unlock_nucypher_keystore,
unlock_signer_account
unlock_signer_account,
)
from nucypher.cli.literature import (
CONFIRM_OVERWRITE_EVENTS_CSV_FILE,
CONNECTING_TO_BLOCKCHAIN,
ETHERSCAN_FLAG_DISABLED_WARNING,
ETHERSCAN_FLAG_ENABLED_WARNING,
FEDERATED_WARNING,
LOCAL_REGISTRY_ADVISORY,
NO_HARDWARE_WALLET_WARNING,
PRODUCTION_REGISTRY_ADVISORY,
CONFIRM_OVERWRITE_EVENTS_CSV_FILE
)
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.utilities.emitters import StdoutEmitter
from nucypher.utilities.events import write_events_to_csv_file
@ -86,7 +85,6 @@ def make_cli_character(character_config,
maybe_sage_node = character_config.known_node_class.from_teacher_uri(
teacher_uri=teacher_uri,
min_stake=min_stake,
federated_only=character_config.federated_only,
network_middleware=character_config.network_middleware,
registry=character_config.registry
)
@ -100,10 +98,6 @@ def make_cli_character(character_config,
# Post-Init
#
# Federated
if character_config.federated_only:
emitter.message(FEDERATED_WARNING, color='yellow')
emitter.message(f"Loaded {CHARACTER.__class__.__name__} ({CHARACTER.domain})", color='green')
return CHARACTER