mirror of https://github.com/nucypher/nucypher.git
Hook up CLI with JSON Character control; Extract CLI actions module for shared config setup and tear down logic.
parent
89c5f15ec0
commit
f1cd291df2
|
@ -1,8 +1,12 @@
|
|||
import shutil
|
||||
from typing import List
|
||||
|
||||
import click
|
||||
from nacl.exceptions import CryptoError
|
||||
from twisted.logger import Logger
|
||||
|
||||
from nucypher.blockchain.eth.registry import EthereumContractRegistry
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
|
||||
|
||||
|
@ -20,6 +24,19 @@ Delete {}?'''
|
|||
LOG = Logger('cli.actions')
|
||||
|
||||
|
||||
def load_seednodes(min_stake: int, federated_only: bool, teacher_uris: list = None) -> List[Ursula]:
|
||||
teacher_nodes = list()
|
||||
if teacher_uris is None:
|
||||
# Default teacher nodes can be placed here
|
||||
return teacher_nodes
|
||||
for uri in teacher_uris:
|
||||
teacher_node = Ursula.from_teacher_uri(teacher_uri=uri,
|
||||
min_stake=min_stake,
|
||||
federated_only=federated_only)
|
||||
teacher_nodes.append(teacher_node)
|
||||
return teacher_nodes
|
||||
|
||||
|
||||
def destroy_system_configuration(config_class,
|
||||
config_file=None,
|
||||
network=None,
|
||||
|
@ -56,3 +73,29 @@ def destroy_system_configuration(config_class,
|
|||
message = "Deleted configuration files at {}".format(character_config.config_root)
|
||||
click.secho(message, fg='green')
|
||||
log.debug(message)
|
||||
|
||||
|
||||
def unlock_keyring(configuration, password):
|
||||
try:
|
||||
click.secho("Decrypting keyring...", fg='blue')
|
||||
configuration.keyring.unlock(password=password)
|
||||
except CryptoError:
|
||||
raise configuration.keyring.AuthenticationFailed
|
||||
|
||||
|
||||
def connect_to_blockchain(configuration, recompile_contracts: bool = False):
|
||||
try:
|
||||
configuration.connect_to_blockchain(recompile_contracts=recompile_contracts)
|
||||
configuration.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)
|
||||
|
||||
|
||||
def forget(configuration):
|
||||
"""Forget all known nodes via storages"""
|
||||
click.confirm("Permanently delete all known node data?", abort=True)
|
||||
configuration.forget_nodes()
|
||||
message = "Removed all stored node node metadata and certificates"
|
||||
click.secho(message=message, fg='red')
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import datetime
|
||||
import os
|
||||
from base64 import b64encode
|
||||
|
||||
import click
|
||||
import maya
|
||||
from nacl.exceptions import CryptoError
|
||||
|
||||
from nucypher.characters.banners import ALICE_BANNER
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.cli.actions import destroy_system_configuration
|
||||
from nucypher.cli import actions, painting
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.painting import paint_configuration
|
||||
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE
|
||||
|
@ -79,44 +76,35 @@ def alice(click_config,
|
|||
if not config_root: # Flag
|
||||
config_root = click_config.config_file # Envvar
|
||||
|
||||
alice_config = AliceConfiguration.generate(password=click_config.get_password(confirm=True),
|
||||
config_root=config_root,
|
||||
rest_host="localhost",
|
||||
domains={network} if network else None,
|
||||
federated_only=federated_only,
|
||||
no_registry=True, # Yes we have no registry,
|
||||
registry_filepath=registry_filepath,
|
||||
provider_uri=provider_uri,
|
||||
)
|
||||
new_alice_config = AliceConfiguration.generate(password=click_config.get_password(confirm=True),
|
||||
config_root=config_root,
|
||||
rest_host="localhost",
|
||||
domains={network} if network else None,
|
||||
federated_only=federated_only,
|
||||
no_registry=True, # Yes we have no registry,
|
||||
registry_filepath=registry_filepath,
|
||||
provider_uri=provider_uri)
|
||||
|
||||
if not quiet:
|
||||
click.secho("Generated keyring {}".format(alice_config.keyring_dir), fg='green')
|
||||
click.secho("Saved configuration file {}".format(alice_config.config_file_location), fg='green')
|
||||
|
||||
# Give the use a suggestion as to what to do next...
|
||||
how_to_run_message = "\nTo run an Alice node from the default configuration filepath run: \n\n'{}'\n"
|
||||
suggested_command = 'nucypher alice run'
|
||||
if config_root is not None:
|
||||
config_file_location = os.path.join(config_root, config_file or AliceConfiguration.CONFIG_FILENAME)
|
||||
suggested_command += ' --config-file {}'.format(config_file_location)
|
||||
click.secho(how_to_run_message.format(suggested_command), fg='green')
|
||||
return # FIN
|
||||
painting.paint_new_installation_help(new_configuration=new_alice_config,
|
||||
config_root=config_root,
|
||||
config_file=config_file)
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
destroy_system_configuration(config_class=AliceConfiguration,
|
||||
config_file=config_file,
|
||||
network=network,
|
||||
config_root=config_root,
|
||||
force=force)
|
||||
actions.destroy_system_configuration(config_class=AliceConfiguration,
|
||||
config_file=config_file,
|
||||
network=network,
|
||||
config_root=config_root,
|
||||
force=force)
|
||||
if not quiet:
|
||||
click.secho("Destroyed {}".format(config_root))
|
||||
return
|
||||
|
@ -129,8 +117,8 @@ def alice(click_config,
|
|||
alice_config = AliceConfiguration(dev_mode=True,
|
||||
domains={network},
|
||||
provider_uri=provider_uri,
|
||||
federated_only=True,
|
||||
)
|
||||
federated_only=True)
|
||||
|
||||
else:
|
||||
alice_config = AliceConfiguration.from_configuration_file(
|
||||
filepath=config_file,
|
||||
|
@ -138,34 +126,26 @@ def alice(click_config,
|
|||
rest_port=discovery_port,
|
||||
provider_uri=provider_uri)
|
||||
|
||||
# Teacher
|
||||
teacher_nodes = list()
|
||||
if teacher_uri:
|
||||
teacher_node = Ursula.from_teacher_uri(teacher_uri=teacher_uri,
|
||||
min_stake=min_stake,
|
||||
federated_only=alice_config.federated_only)
|
||||
teacher_nodes.append(teacher_node)
|
||||
|
||||
if not dev:
|
||||
# Keyring
|
||||
try:
|
||||
click.secho("Decrypting keyring...", fg='blue')
|
||||
alice_config.keyring.unlock(password=click_config.get_password())
|
||||
except CryptoError:
|
||||
raise alice_config.keyring.AuthenticationFailed
|
||||
finally:
|
||||
click_config.alice_config = alice_config
|
||||
actions.unlock_keyring(password=click_config.get_password(), configuration=alice_config)
|
||||
|
||||
# Teacher Ursula
|
||||
teacher_uris = [teacher_uri] if teacher_uri else list()
|
||||
teacher_nodes = actions.load_seednodes(teacher_uris=teacher_uris,
|
||||
min_stake=min_stake,
|
||||
federated_only=federated_only)
|
||||
# Produce
|
||||
ALICE = alice_config(known_nodes=teacher_nodes)
|
||||
|
||||
if action == "run":
|
||||
return ALICE.control.run(http_port=http_port, dry_run=dry_run)
|
||||
click.secho(f"Alice Verifying Key {bytes(ALICE.stamp).hex()}", fg="green", bold=True)
|
||||
return ALICE.control.start_wsgi_controller(http_port=http_port, dry_run=dry_run)
|
||||
|
||||
elif action == "view":
|
||||
"""Paint an existing configuration to the console"""
|
||||
paint_configuration(config_filepath=config_file or alice_config.config_file_location)
|
||||
return
|
||||
json_config = AliceConfiguration._read_configuration_file(filepath=config_file or alice_config.config_file_location)
|
||||
paint_configuration(json_config=json_config)
|
||||
return json_config
|
||||
|
||||
elif action == "create-policy":
|
||||
if not all((bob_verifying_key, bob_encrypting_key, label)):
|
||||
|
@ -187,7 +167,6 @@ def alice(click_config,
|
|||
elif action == "derive-policy":
|
||||
response = ALICE.control.derive_policy(label=label)
|
||||
click.secho(response)
|
||||
# click.secho(f"Created new Policy with label {label} | {policy_encrypting_key}", fg='green')
|
||||
return response
|
||||
|
||||
elif action == "grant":
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
import json
|
||||
import os
|
||||
from base64 import b64encode
|
||||
|
||||
import click
|
||||
import requests
|
||||
from nacl.exceptions import CryptoError
|
||||
|
||||
from hendrix.deploy.base import HendrixDeploy
|
||||
|
||||
from nucypher.characters.banners import BOB_BANNER
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.cli.actions import destroy_system_configuration
|
||||
from nucypher.cli import actions, painting
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.painting import paint_configuration
|
||||
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE
|
||||
from nucypher.config.characters import BobConfiguration
|
||||
from nucypher.config.constants import GLOBAL_DOMAIN
|
||||
from nucypher.crypto.kits import UmbralMessageKit
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
||||
|
||||
|
@ -75,27 +67,18 @@ def bob(click_config,
|
|||
if not config_root: # Flag
|
||||
config_root = click_config.config_file # Envvar
|
||||
|
||||
bob_config = BobConfiguration.generate(password=click_config.get_password(confirm=True),
|
||||
config_root=config_root,
|
||||
rest_host="localhost",
|
||||
domains={network} if network else None,
|
||||
federated_only=federated_only,
|
||||
no_registry=True, # Yes we have no registry,
|
||||
registry_filepath=registry_filepath,
|
||||
provider_uri=provider_uri,
|
||||
)
|
||||
new_bob_config = BobConfiguration.generate(password=click_config.get_password(confirm=True),
|
||||
config_root=config_root,
|
||||
rest_host="localhost",
|
||||
domains={network} if network else None,
|
||||
federated_only=federated_only,
|
||||
no_registry=True, # Yes we have no registry,
|
||||
registry_filepath=registry_filepath,
|
||||
provider_uri=provider_uri,
|
||||
)
|
||||
|
||||
if not quiet:
|
||||
click.secho("Generated keyring {}".format(bob_config.keyring_dir), fg='green')
|
||||
click.secho("Saved configuration file {}".format(bob_config.config_file_location), fg='green')
|
||||
|
||||
# Give the use a suggestion as to what to do next...
|
||||
how_to_run_message = "\nTo run an Bob node from the default configuration filepath run: \n\n'{}'\n"
|
||||
suggested_command = 'nucypher bob run'
|
||||
if config_root is not None:
|
||||
config_file_location = os.path.join(config_root, config_file or BobConfiguration.CONFIG_FILENAME)
|
||||
suggested_command += ' --config-file {}'.format(config_file_location)
|
||||
click.secho(how_to_run_message.format(suggested_command), fg='green')
|
||||
painting.paint_new_installation_help(new_configuration=new_bob_config)
|
||||
return # FIN
|
||||
|
||||
else:
|
||||
|
@ -108,11 +91,11 @@ def bob(click_config,
|
|||
message = "'nucypher ursula destroy' cannot be used in --dev mode"
|
||||
raise click.BadOptionUsage(option_name='--dev', message=message)
|
||||
|
||||
destroy_system_configuration(config_class=BobConfiguration,
|
||||
config_file=config_file,
|
||||
network=network,
|
||||
config_root=config_root,
|
||||
force=force)
|
||||
actions.destroy_system_configuration(config_class=BobConfiguration,
|
||||
config_file=config_file,
|
||||
network=network,
|
||||
config_root=config_root,
|
||||
force=force)
|
||||
if not quiet:
|
||||
click.secho("Destroyed {}".format(config_root))
|
||||
return
|
||||
|
@ -134,13 +117,11 @@ def bob(click_config,
|
|||
rest_port=discovery_port,
|
||||
provider_uri=provider_uri)
|
||||
|
||||
# Teacher
|
||||
teacher_nodes = list()
|
||||
if teacher_uri:
|
||||
teacher_node = Ursula.from_teacher_uri(teacher_uri=teacher_uri,
|
||||
min_stake=min_stake,
|
||||
federated_only=bob_config.federated_only)
|
||||
teacher_nodes.append(teacher_node)
|
||||
# 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)
|
||||
|
||||
# Produce
|
||||
BOB = bob_config(known_nodes=teacher_nodes)
|
||||
|
@ -148,47 +129,30 @@ def bob(click_config,
|
|||
if action == "run":
|
||||
|
||||
if not dev:
|
||||
# Keyring
|
||||
try:
|
||||
click.secho("Decrypting keyring...", fg='blue')
|
||||
bob_config.keyring.unlock(password=click_config.get_password())
|
||||
except CryptoError:
|
||||
raise bob_config.keyring.AuthenticationFailed
|
||||
finally:
|
||||
click_config.bob_config = bob_config
|
||||
|
||||
# Bob Control
|
||||
bob_control = BOB.make_wsgi_app()
|
||||
click.secho("Starting Bob Character Control...")
|
||||
actions.unlock_keyring(configuration=bob_config, password=click_config.get_password())
|
||||
|
||||
click.secho(f"Bob Verifying Key {bytes(BOB.stamp).hex()}", fg="green", bold=True)
|
||||
click.secho(f"Bob Encrypting Key {bytes(BOB.public_keys(DecryptingPower)).hex()}", fg="blue", bold=True)
|
||||
|
||||
# Run
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
hx_deployer = HendrixDeploy(action="start", options={"wsgi": bob_control, "http_port": http_port})
|
||||
hx_deployer.run() # <--- Blocking Call to Reactor
|
||||
BOB.control.start_wsgi_control(dry_run=dry_run, http_port=http_port)
|
||||
|
||||
elif action == "view":
|
||||
"""Paint an existing configuration to the console"""
|
||||
paint_configuration(config_filepath=config_file or bob_config.config_file_location)
|
||||
return
|
||||
json_config = BobConfiguration._read_configuration_file(filepath=config_file or bob_config.config_file_location)
|
||||
paint_configuration(json_config=json_config)
|
||||
return json_config
|
||||
|
||||
elif action == "retrieve":
|
||||
# bob_request_data = {
|
||||
# 'label': b64encode(label).decode(),
|
||||
# 'policy_encrypting_key': policy_encrypting_key,
|
||||
# 'alice_signing_pubkey': alice_encrypting_key,
|
||||
# # 'message_kit': b64encode(bob_message_kit.to_bytes()).decode(), # TODO
|
||||
# }
|
||||
|
||||
stdin_text = click.get_text_stream('stdin')
|
||||
message_kit = UmbralMessageKit.from_bytes(bytes(stdin_text))
|
||||
result = BOB.retrieve(label=label, alice_verifying_key=alice_encrypting_key, message_kit=message_kit)
|
||||
click.secho(result)
|
||||
return
|
||||
bob_request_data = {
|
||||
'label': b64encode(label).decode(),
|
||||
'policy_encrypting_pubkey': policy_encrypting_key,
|
||||
'alice_signing_pubkey': alice_encrypting_key,
|
||||
# 'message_kit': b64encode(bob_message_kit.to_bytes()).decode(), # TODO
|
||||
}
|
||||
|
||||
response = BOB.control.retrieve(request=bob_request_data)
|
||||
click.secho(response)
|
||||
return response
|
||||
|
||||
else:
|
||||
raise click.BadArgumentUsage(f"No such argument {action}")
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import click
|
||||
from umbral.keys import UmbralPublicKey
|
||||
|
||||
from hendrix.deploy.base import HendrixDeploy
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.cli.types import NETWORK_PORT
|
||||
from umbral.keys import UmbralPublicKey
|
||||
|
||||
ENRICO_BANNER = r"""
|
||||
___
|
||||
|
@ -19,7 +18,7 @@ the Encryptor.
|
|||
@click.command()
|
||||
@click.argument('action')
|
||||
@click.option('--dry-run', '-x', help="Execute normally without actually starting the node", is_flag=True)
|
||||
@click.option('--http-port', help="The host port to run Moe HTTP services on", type=NETWORK_PORT, default=5151) # TODO
|
||||
@click.option('--http-port', help="The host port to run Moe HTTP services on", type=NETWORK_PORT, default=5151) # TODO: default ports
|
||||
@click.option('--policy-encrypting-key', help="Encrypting Public Key for Policy as hexidecimal string", type=click.STRING)
|
||||
def enrico(action, policy_encrypting_key, dry_run, http_port):
|
||||
"""
|
||||
|
@ -31,19 +30,7 @@ def enrico(action, policy_encrypting_key, dry_run, http_port):
|
|||
if action == 'run': # Forest
|
||||
policy_encrypting_key = UmbralPublicKey.from_bytes(bytes.fromhex(policy_encrypting_key))
|
||||
ENRICO = Enrico(policy_encrypting_key=policy_encrypting_key)
|
||||
|
||||
# Enrico Control
|
||||
enrico_control = ENRICO.make_wsgi_app()
|
||||
click.secho("Starting Enrico Character Control...")
|
||||
|
||||
click.secho(f"Enrico Verifying Key {bytes(ENRICO.stamp).hex()}", fg="green", bold=True)
|
||||
|
||||
# Run
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
hx_deployer = HendrixDeploy(action="start", options={"wsgi": enrico_control, "http_port": http_port})
|
||||
hx_deployer.run() # <--- Blocking Call to Reactor
|
||||
ENRICO.control.start_wsgi_conrol(http_port=http_port, dry_run=dry_run)
|
||||
|
||||
else:
|
||||
raise click.BadArgumentUsage
|
||||
|
|
|
@ -30,6 +30,7 @@ from twisted.logger import globalLogPublisher
|
|||
from nucypher.blockchain.eth.constants import MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS
|
||||
from nucypher.blockchain.eth.registry import EthereumContractRegistry
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.cli import actions
|
||||
from nucypher.cli.actions import destroy_system_configuration
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.painting import paint_configuration
|
||||
|
@ -88,6 +89,7 @@ the Untrusted Re-Encryption Proxy.
|
|||
@click.option('--config-file', help="Path to configuration file", type=EXISTING_READABLE_FILE)
|
||||
@click.option('--metadata-dir', help="Custom known metadata directory", type=EXISTING_WRITABLE_DIRECTORY)
|
||||
@click.option('--provider-uri', help="Blockchain provider's URI", type=click.STRING)
|
||||
@click.option('--recompile-solidity', help="Compile solidity from source when making a web3 connection", is_flag=True)
|
||||
@click.option('--no-registry', help="Skip importing the default contract registry", is_flag=True)
|
||||
@click.option('--registry-filepath', help="Custom contract registry filepath", type=EXISTING_READABLE_FILE)
|
||||
@nucypher_click_config
|
||||
|
@ -112,6 +114,7 @@ def ursula(click_config,
|
|||
config_file,
|
||||
metadata_dir, # TODO: Start nodes from an additional existing metadata dir
|
||||
provider_uri,
|
||||
recompile_solidity,
|
||||
no_registry,
|
||||
registry_filepath
|
||||
) -> None:
|
||||
|
@ -260,21 +263,11 @@ def ursula(click_config,
|
|||
# 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
|
||||
actions.unlock_keyring(configuration=ursula_config,
|
||||
password=click_config.get_password())
|
||||
|
||||
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)
|
||||
actions.connect_to_blockchain(configuration=ursula_config, recompile_contracts=recompile_solidity)
|
||||
|
||||
click_config.ursula_config = ursula_config # Pass Ursula's config onto staking sub-command
|
||||
|
||||
|
@ -295,17 +288,16 @@ def ursula(click_config,
|
|||
#
|
||||
# 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)
|
||||
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)
|
||||
|
||||
|
||||
#
|
||||
# Produce - Step 2
|
||||
#
|
||||
ursula = ursula_config(known_nodes=teacher_nodes, lonely=lonely)
|
||||
URSULA = ursula_config(known_nodes=teacher_nodes, lonely=lonely)
|
||||
|
||||
# GO!
|
||||
try:
|
||||
|
@ -314,15 +306,15 @@ def ursula(click_config,
|
|||
# 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)
|
||||
click.secho("Running Ursula {} on {}".format(URSULA, URSULA.rest_interface), fg='green', bold=True)
|
||||
if not debug:
|
||||
stdio.StandardIO(UrsulaCommandProtocol(ursula=ursula))
|
||||
stdio.StandardIO(UrsulaCommandProtocol(ursula=URSULA))
|
||||
|
||||
if dry_run:
|
||||
# That's all folks!
|
||||
return
|
||||
|
||||
ursula.get_deployer().run() # <--- Blocking Call (Reactor)
|
||||
URSULA.get_deployer().run() # <--- Blocking Call (Reactor)
|
||||
|
||||
except Exception as e:
|
||||
ursula_config.log.critical(str(e))
|
||||
|
@ -341,23 +333,20 @@ def ursula(click_config,
|
|||
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)
|
||||
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
|
||||
json_config = UrsulaConfiguration._read_configuration_file(filepath=config_file or ursula_config.config_file_location)
|
||||
paint_configuration(json_config=json_config)
|
||||
return json_config
|
||||
|
||||
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')
|
||||
actions.forget(configuration=ursula_config)
|
||||
return
|
||||
|
||||
else:
|
||||
|
@ -413,7 +402,7 @@ def stake(click_config,
|
|||
click.echo(row)
|
||||
|
||||
click.echo("Select ethereum address")
|
||||
account_selection = click.prompt("Enter 0-{}".format(len(ur.accounts)), type=click.INT)
|
||||
account_selection = click.prompt("Enter 0-{}".format(len(ursula_config.accounts)), type=click.INT)
|
||||
address = click_config.accounts[account_selection]
|
||||
|
||||
if action == 'list':
|
||||
|
|
|
@ -15,7 +15,7 @@ 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 collections
|
||||
import os
|
||||
|
||||
import click
|
||||
|
@ -23,7 +23,6 @@ from constant_sorrow.constants import NO_PASSWORD
|
|||
from twisted.logger import Logger
|
||||
from twisted.logger import globalLogPublisher
|
||||
|
||||
from nucypher.characters.banners import BANNER
|
||||
from nucypher.config.constants import NUCYPHER_SENTRY_ENDPOINT
|
||||
from nucypher.utilities.logging import (
|
||||
logToSentry,
|
||||
|
@ -67,12 +66,39 @@ class NucypherClickConfig:
|
|||
return self.__keyring_password
|
||||
|
||||
|
||||
# Register the above click configuration class as a decorator
|
||||
class NucypherDeployerClickConfig(NucypherClickConfig):
|
||||
|
||||
# Deploy Environment Variables
|
||||
miner_escrow_deployment_secret = os.environ.get("NUCYPHER_MINER_ESCROW_SECRET", None)
|
||||
policy_manager_deployment_secret = os.environ.get("NUCYPHER_POLICY_MANAGER_SECRET", None)
|
||||
user_escrow_proxy_deployment_secret = os.environ.get("NUCYPHER_USER_ESCROW_PROXY_SECRET", None)
|
||||
|
||||
Secrets = collections.namedtuple('Secrets', ('miner_secret', 'policy_secret', 'escrow_proxy_secret'))
|
||||
|
||||
def collect_deployment_secrets(self) -> Secrets:
|
||||
|
||||
miner_secret = self.miner_escrow_deployment_secret
|
||||
if not miner_secret:
|
||||
miner_secret = click.prompt('Enter MinerEscrow Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
policy_secret = self.policy_manager_deployment_secret
|
||||
if not policy_secret:
|
||||
policy_secret = click.prompt('Enter PolicyManager Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
escrow_proxy_secret = self.user_escrow_proxy_deployment_secret
|
||||
if not escrow_proxy_secret:
|
||||
escrow_proxy_secret = click.prompt('Enter UserEscrowProxy Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
secrets = self.Secrets(miner_secret=miner_secret, # type: str
|
||||
policy_secret=policy_secret, # type: str
|
||||
escrow_proxy_secret=escrow_proxy_secret # type: str
|
||||
)
|
||||
return secrets
|
||||
|
||||
|
||||
# Register the above click configuration classes as a decorators
|
||||
nucypher_click_config = click.make_pass_decorator(NucypherClickConfig, ensure=True)
|
||||
|
||||
|
||||
def echo_version(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
click.secho(BANNER, bold=True)
|
||||
ctx.exit()
|
||||
nucypher_deployer_config = click.make_pass_decorator(NucypherDeployerClickConfig, ensure=True)
|
||||
|
|
|
@ -15,76 +15,17 @@ 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 collections
|
||||
import json
|
||||
import os
|
||||
|
||||
import click
|
||||
from twisted.logger import Logger
|
||||
from twisted.logger import globalLogPublisher
|
||||
|
||||
from nucypher.blockchain.eth.actors import Deployer
|
||||
from nucypher.blockchain.eth.chains import Blockchain
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
from nucypher.characters.banners import BANNER
|
||||
from nucypher.cli.config import nucypher_deployer_config
|
||||
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, EXISTING_READABLE_FILE
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from nucypher.utilities.logging import getTextFileObserver
|
||||
|
||||
|
||||
#
|
||||
# Click Eager Functions
|
||||
#
|
||||
|
||||
def echo_version(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
click.secho(BANNER, bold=True)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
class NucypherDeployerClickConfig:
|
||||
|
||||
# Environment Variables
|
||||
log_to_file = os.environ.get("NUCYPHER_FILE_LOGS", True)
|
||||
miner_escrow_deployment_secret = os.environ.get("NUCYPHER_MINER_ESCROW_SECRET", None)
|
||||
policy_manager_deployment_secret = os.environ.get("NUCYPHER_POLICY_MANAGER_SECRET", None)
|
||||
user_escrow_proxy_deployment_secret = os.environ.get("NUCYPHER_USER_ESCROW_PROXY_SECRET", None)
|
||||
|
||||
Secrets = collections.namedtuple('Secrets', ('miner_secret', 'policy_secret', 'escrow_proxy_secret'))
|
||||
|
||||
def __init__(self):
|
||||
if self.log_to_file:
|
||||
globalLogPublisher.addObserver(getTextFileObserver())
|
||||
self.log = Logger(self.__class__.__name__)
|
||||
|
||||
def collect_deployment_secrets(self) -> Secrets:
|
||||
|
||||
miner_secret = self.miner_escrow_deployment_secret
|
||||
if not miner_secret:
|
||||
miner_secret = click.prompt('Enter MinerEscrow Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
policy_secret = self.policy_manager_deployment_secret
|
||||
if not policy_secret:
|
||||
policy_secret = click.prompt('Enter PolicyManager Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
escrow_proxy_secret = self.user_escrow_proxy_deployment_secret
|
||||
if not escrow_proxy_secret:
|
||||
escrow_proxy_secret = click.prompt('Enter UserEscrowProxy Deployment Secret', hide_input=True,
|
||||
confirmation_prompt=True)
|
||||
|
||||
secrets = self.Secrets(miner_secret=miner_secret, # type: str
|
||||
policy_secret=policy_secret, # type: str
|
||||
escrow_proxy_secret=escrow_proxy_secret # type: str
|
||||
)
|
||||
return secrets
|
||||
|
||||
|
||||
# Register the above class as a decorator
|
||||
nucypher_deployer_config = click.make_pass_decorator(NucypherDeployerClickConfig, ensure=True)
|
||||
|
||||
|
||||
@click.command()
|
||||
|
|
|
@ -20,9 +20,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
import click
|
||||
|
||||
from nucypher.characters.banners import BANNER
|
||||
from nucypher.cli import status
|
||||
from nucypher.cli.characters import moe, bob, ursula, alice, enrico
|
||||
from nucypher.cli.config import echo_version, nucypher_click_config
|
||||
from nucypher.cli import moe, ursula, status, alice, bob, enrico
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.painting import BANNER, echo_version
|
||||
from nucypher.utilities.logging import GlobalConsoleLogger
|
||||
|
||||
GlobalConsoleLogger.start_if_not_started()
|
||||
|
|
|
@ -14,14 +14,13 @@ 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/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import click
|
||||
import maya
|
||||
from constant_sorrow.constants import NO_KNOWN_NODES
|
||||
|
||||
import nucypher
|
||||
from constant_sorrow.constants import NO_KNOWN_NODES
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
from nucypher.config.constants import SEEDNODES
|
||||
|
||||
|
||||
|
@ -29,6 +28,29 @@ from nucypher.config.constants import SEEDNODES
|
|||
# Paint
|
||||
#
|
||||
|
||||
def echo_version(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
click.secho(BANNER, bold=True)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
def paint_new_installation_help(new_configuration, config_root=None, config_file=None):
|
||||
character_config_class = new_configuration.__class__
|
||||
character_name = character_config_class._CHARACTER_CLASS.__name__.lower()
|
||||
|
||||
click.secho("Generated keyring {}".format(new_configuration.keyring_dir), fg='green')
|
||||
click.secho("Saved configuration file {}".format(new_configuration.config_file_location), fg='green')
|
||||
|
||||
# Give the use a suggestion as to what to do next...
|
||||
suggested_command = f'nucypher {character_name} run'
|
||||
how_to_run_message = f"\nTo run an {character_name.capitalize()} node from the default configuration filepath run: \n\n'{suggested_command}'\n"
|
||||
if config_root is not None:
|
||||
config_file_location = os.path.join(config_root, config_file or character_config_class.CONFIG_FILENAME)
|
||||
suggested_command += ' --config-file {}'.format(config_file_location)
|
||||
click.secho(how_to_run_message.format(suggested_command), fg='green')
|
||||
|
||||
|
||||
def build_fleet_state_status(ursula) -> str:
|
||||
# Build FleetState status line
|
||||
if ursula.known_nodes.checksum is not NO_KNOWN_NODES:
|
||||
|
@ -46,9 +68,8 @@ def build_fleet_state_status(ursula) -> str:
|
|||
return fleet_state
|
||||
|
||||
|
||||
def paint_configuration(config_filepath: str) -> None:
|
||||
json_config = UrsulaConfiguration._read_configuration_file(filepath=config_filepath)
|
||||
click.secho("\n======== Ursula Configuration ======== \n", bold=True)
|
||||
def paint_configuration(json_config: dict) -> None:
|
||||
click.secho("\n======== Character Configuration ======== \n", bold=True)
|
||||
for key, value in json_config.items():
|
||||
click.secho("{} = {}".format(key, value))
|
||||
|
||||
|
|
Loading…
Reference in New Issue