Take back control of CLI imports, namespaces, and organization. Work on a CLI and tidy CLI.

pull/1983/head
Kieran Prasch 2020-05-12 18:21:39 -07:00
parent 5992e05897
commit 2e9da4ef31
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
23 changed files with 763 additions and 702 deletions

View File

@ -23,8 +23,12 @@ 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, DECRYPTING_CHARACTER_KEYRING,
GENERIC_PASSWORD_PROMPT)
from nucypher.cli.literature import (
COLLECT_ETH_PASSWORD,
COLLECT_NUCYPHER_PASSWORD,
DECRYPTING_CHARACTER_KEYRING,
GENERIC_PASSWORD_PROMPT
)
from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD
from nucypher.config.node import CharacterConfiguration

View File

@ -26,9 +26,15 @@ from eth_utils.address import is_checksum_address
from json.decoder import JSONDecodeError
from nucypher.blockchain.eth.clients import NuCypherGethGoerliProcess
from nucypher.cli.literature import (CHARACTER_DESTRUCTION, CONFIRM_FORGET_NODES, INVALID_CONFIGURATION_FILE_WARNING,
INVALID_JSON_IN_CONFIGURATION_WARNING, SUCCESSFUL_DESTRUCTION,
SUCCESSFUL_FORGET_NODES, SUCCESSFUL_UPDATE_CONFIGURATION_VALUES)
from nucypher.cli.literature import (
CHARACTER_DESTRUCTION,
CONFIRM_FORGET_NODES,
INVALID_CONFIGURATION_FILE_WARNING,
INVALID_JSON_IN_CONFIGURATION_WARNING,
SUCCESSFUL_DESTRUCTION,
SUCCESSFUL_FORGET_NODES,
SUCCESSFUL_UPDATE_CONFIGURATION_VALUES
)
from nucypher.config.characters import UrsulaConfiguration

View File

@ -20,10 +20,18 @@ import click
from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID
from nucypher.blockchain.eth.token import NU
from nucypher.cli.literature import (ABORT_DEPLOYMENT, CONFIRM_ENABLE_RESTAKING, CONFIRM_ENABLE_WINDING_DOWN,
CONFIRM_LARGE_STAKE_DURATION, CONFIRM_LARGE_STAKE_VALUE, CONFIRM_RESTAKING_LOCK,
CONFIRM_STAGED_STAKE, RESTAKING_AGREEMENT, RESTAKING_LOCK_AGREEMENT,
WINDING_DOWN_AGREEMENT)
from nucypher.cli.literature import (
ABORT_DEPLOYMENT,
CONFIRM_ENABLE_RESTAKING,
CONFIRM_ENABLE_WINDING_DOWN,
CONFIRM_LARGE_STAKE_DURATION,
CONFIRM_LARGE_STAKE_VALUE,
CONFIRM_RESTAKING_LOCK,
CONFIRM_STAGED_STAKE,
RESTAKING_AGREEMENT,
RESTAKING_LOCK_AGREEMENT,
WINDING_DOWN_AGREEMENT
)
def confirm_deployment(emitter, deployer_interface) -> bool:

View File

@ -26,9 +26,15 @@ from json.decoder import JSONDecodeError
from typing import Dict, List, Optional, Set
from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.cli.literature import (COLLECT_URSULA_IPV4_ADDRESS, CONFIRM_URSULA_IPV4_ADDRESS,
FORCE_DETECT_URSULA_IP_WARNING, NO_DOMAIN_PEERS, SEEDNODE_NOT_STAKING_WARNING,
START_LOADING_SEEDNODES, UNREADABLE_SEEDNODE_ADVISORY)
from nucypher.cli.literature import (
COLLECT_URSULA_IPV4_ADDRESS,
CONFIRM_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

View File

@ -32,10 +32,19 @@ from nucypher.blockchain.eth.registry import InMemoryContractRegistry, Individua
from nucypher.blockchain.eth.signers import Signer
from nucypher.blockchain.eth.token import NU, Stake
from nucypher.cli.actions.config import extract_checksum_address_from_filepath
from nucypher.cli.literature import (GENERIC_SELECT_ACCOUNT, IS_THIS_CORRECT, NO_CONFIGURATIONS_ON_DISK,
NO_DIVISIBLE_STAKES, NO_ETH_ACCOUNTS, NO_STAKES_FOUND,
ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE, PREALLOCATION_STAKE_ADVISORY,
SELECT_NETWORK, SELECT_STAKE, SELECT_STAKING_ACCOUNT_INDEX)
from nucypher.cli.literature import (
GENERIC_SELECT_ACCOUNT,
IS_THIS_CORRECT,
NO_CONFIGURATIONS_ON_DISK,
NO_DIVISIBLE_STAKES,
NO_ETH_ACCOUNTS,
NO_STAKES_FOUND,
ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE,
PREALLOCATION_STAKE_ADVISORY,
SELECT_NETWORK,
SELECT_STAKE,
SELECT_STAKING_ACCOUNT_INDEX
)
from nucypher.cli.painting.staking import paint_stakes
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, NUCYPHER_ENVVAR_WORKER_ADDRESS

View File

@ -15,6 +15,8 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
from distutils.util import strtobool
import click
@ -23,15 +25,33 @@ import shutil
from constant_sorrow.constants import NO_CONTROL_PROTOCOL
from nacl.exceptions import CryptoError
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.interfaces import (
BlockchainDeployerInterface,
BlockchainInterface,
BlockchainInterfaceFactory
)
from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContractRegistry, LocalContractRegistry
from nucypher.cli.actions.auth import get_nucypher_password, unlock_nucypher_keyring
from nucypher.cli.actions.network import load_seednodes
from nucypher.cli.literature import (CONNECTING_TO_BLOCKCHAIN, FEDERATED_WARNING, LOCAL_REGISTRY_ADVISORY,
PRODUCTION_REGISTRY_ADVISORY)
from nucypher.cli.literature import (
CONNECTING_TO_BLOCKCHAIN,
ETHERSCAN_FLAG_DISABLED_WARNING,
ETHERSCAN_FLAG_ENABLED_WARNING,
FEDERATED_WARNING,
LOCAL_REGISTRY_ADVISORY,
NO_HARDWARE_WALLET_WARNING,
PRODUCTION_REGISTRY_ADVISORY
)
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
def setup_emitter(general_config, banner: str = None):
emitter = general_config.emitter
if banner:
emitter.banner(banner)
return emitter
def make_cli_character(character_config,
emitter,
unlock_keyring: bool = True,
@ -154,10 +174,40 @@ def connect_to_blockchain(provider_uri, emitter, debug: bool = False, light: boo
raise click.Abort
def initialize_deployer_interface(poa, provider_uri, emitter, ignore_solidity_check, gas_strategy=None):
if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
deployer_interface = BlockchainDeployerInterface(provider_uri=provider_uri,
poa=poa,
ignore_solidity_check=ignore_solidity_check,
gas_strategy=gas_strategy)
BlockchainInterfaceFactory.register_interface(interface=deployer_interface, sync=False,
emitter=emitter)
else:
deployer_interface = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
deployer_interface.connect()
return deployer_interface
def get_env_bool(var_name: str, default: bool) -> bool:
if var_name in os.environ:
# TODO: which is better: to fail on an incorrect envvar, or to use the default?
# Currently doing the former.
return strtobool(os.environ[var_name])
else:
return default
return default
def ensure_config_root(config_root):
"""Ensure config root exists, because we need a default place to put output files."""
config_root = config_root or DEFAULT_CONFIG_ROOT
if not os.path.exists(config_root):
os.makedirs(config_root)
def _pre_launch_warnings(emitter, etherscan, hw_wallet):
if not hw_wallet:
emitter.echo(NO_HARDWARE_WALLET_WARNING, color='yellow')
if etherscan:
emitter.echo(ETHERSCAN_FLAG_ENABLED_WARNING, color='yellow')
else:
emitter.echo(ETHERSCAN_FLAG_DISABLED_WARNING, color='yellow')

View File

@ -19,25 +19,49 @@ import click
import os
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION, NO_PASSWORD
from nucypher.characters.banners import ALICE_BANNER
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.characters.control.interfaces import AliceInterface
from nucypher.cli.actions.auth import get_client_password, get_nucypher_password
from nucypher.cli.actions.config import (destroy_configuration, get_or_update_configuration, get_provider_process,
handle_missing_configuration_file)
from nucypher.cli.actions.config import (
destroy_configuration,
get_or_update_configuration,
get_provider_process,
handle_missing_configuration_file
)
from nucypher.cli.actions.select import select_client_account
from nucypher.cli.actions.utils import make_cli_character
from nucypher.cli.actions.utils import make_cli_character, setup_emitter
from nucypher.cli.commands.deploy import option_gas_strategy
from nucypher.cli.config import group_general_config
from nucypher.cli.options import (group_options, option_config_file, option_config_root, option_controller_port,
option_dev, option_discovery_port, option_dry_run, option_federated_only,
option_force, option_geth, option_hw_wallet, option_light, option_m,
option_middleware, option_min_stake, option_n, option_network, option_poa,
option_provider_uri, option_registry_filepath, option_signer_uri, option_teacher_uri)
from nucypher.cli.options import (
group_options,
option_config_file,
option_config_root,
option_controller_port,
option_dev,
option_discovery_port,
option_dry_run,
option_federated_only,
option_force,
option_geth,
option_hw_wallet,
option_light,
option_m,
option_middleware,
option_min_stake,
option_n,
option_network,
option_poa,
option_provider_uri,
option_registry_filepath,
option_signer_uri,
option_teacher_uri
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS
from nucypher.config.characters import AliceConfiguration
from nucypher.config.constants import NUCYPHER_ENVVAR_ALICE_ETH_PASSWORD
from nucypher.config.keyring import NucypherKeyring
from nucypher.network.middleware import RestMiddleware
from nucypher.utilities.sandbox.constants import TEMPORARY_DOMAIN
option_bob_verifying_key = click.option(
@ -55,8 +79,19 @@ class AliceConfigOptions:
__option_name__ = 'config_options'
def __init__(self, dev, network, provider_uri, geth, federated_only, discovery_port,
pay_with, registry_filepath, middleware, gas_strategy, signer_uri):
def __init__(self,
dev: bool,
network: str,
provider_uri: str,
geth: bool,
federated_only: bool,
discovery_port: int,
pay_with: str,
registry_filepath: str,
middleware: RestMiddleware,
gas_strategy: str,
signer_uri: str
):
if federated_only and geth:
raise click.BadOptionUsage(
@ -145,7 +180,7 @@ class AliceFullConfigOptions:
__option_name__ = 'full_config_options'
def __init__(self, config_options, poa, light, m, n, duration_periods):
def __init__(self, config_options, poa: bool, light: bool, m: int, n: int, duration_periods: int):
self.config_options = config_options
self.poa = poa
self.light = light
@ -153,7 +188,7 @@ class AliceFullConfigOptions:
self.n = n
self.duration_periods = duration_periods
def generate_config(self, emitter, config_root):
def generate_config(self, emitter: StdoutEmitter, config_root: str) -> AliceConfiguration:
opts = self.config_options
@ -210,15 +245,15 @@ group_full_config_options = group_options(
light=option_light,
m=option_m,
n=option_n,
duration_periods=option_duration_periods,
)
duration_periods=option_duration_periods
)
class AliceCharacterOptions:
__option_name__ = 'character_options'
def __init__(self, config_options, hw_wallet, teacher_uri, min_stake):
def __init__(self, config_options: AliceFullConfigOptions, hw_wallet: bool, teacher_uri: str, min_stake: int):
self.config_options = config_options
self.hw_wallet = hw_wallet
self.teacher_uri = teacher_uri
@ -262,15 +297,12 @@ group_character_options = group_options(
hw_wallet=option_hw_wallet,
teacher_uri=option_teacher_uri,
min_stake=option_min_stake,
)
)
@click.group()
def alice():
"""
"Alice the Policy Authority" management commands.
"""
pass
""""Alice the Policy Authority" management commands."""
@alice.command()
@ -278,10 +310,8 @@ def alice():
@option_config_root
@group_general_config
def init(general_config, full_config_options, config_root):
"""
Create a brand new persistent Alice.
"""
emitter = _setup_emitter(general_config)
"""Create a brand new persistent Alice."""
emitter = setup_emitter(general_config)
if not config_root:
config_root = general_config.config_root
new_alice_config = full_config_options.generate_config(emitter, config_root)
@ -293,10 +323,8 @@ def init(general_config, full_config_options, config_root):
@group_general_config
@group_full_config_options
def config(general_config, config_file, full_config_options):
"""
View and optionally update existing Alice's configuration.
"""
emitter = _setup_emitter(general_config)
"""View and optionally update existing Alice's configuration."""
emitter = setup_emitter(general_config)
configuration_file_location = config_file or AliceConfiguration.default_filepath()
emitter.echo(f"Alice Configuration {configuration_file_location} \n {'='*55}")
return get_or_update_configuration(emitter=emitter,
@ -311,10 +339,8 @@ def config(general_config, config_file, full_config_options):
@option_force
@group_general_config
def destroy(general_config, config_options, config_file, force):
"""
Delete existing Alice's configuration.
"""
emitter = _setup_emitter(general_config)
"""Delete existing Alice's configuration."""
emitter = setup_emitter(general_config)
alice_config = config_options.create_config(emitter, config_file)
return destroy_configuration(emitter, character_config=alice_config, force=force)
@ -326,10 +352,10 @@ def destroy(general_config, config_options, config_file, force):
@group_general_config
@group_character_options
def run(general_config, character_options, config_file, controller_port, dry_run):
"""
Start Alice's web controller.
"""
emitter = _setup_emitter(general_config)
"""Start Alice's web controller."""
# Setup
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)
try:
@ -362,10 +388,8 @@ def run(general_config, character_options, config_file, controller_port, dry_run
@option_config_file
@group_general_config
def public_keys(general_config, character_options, config_file):
"""
Obtain Alice's public verification and encryption keys.
"""
emitter = _setup_emitter(general_config)
"""Obtain Alice's public verification and encryption keys."""
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
response = ALICE.controller.public_keys()
return response
@ -377,10 +401,8 @@ def public_keys(general_config, character_options, config_file):
@option_config_file
@group_general_config
def derive_policy_pubkey(general_config, label, character_options, config_file):
"""
Get a policy public key from a policy label.
"""
emitter = _setup_emitter(general_config)
"""Get a policy public key from a policy label."""
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
return ALICE.controller.derive_policy_encrypting_key(label=label)
@ -391,21 +413,19 @@ def derive_policy_pubkey(general_config, label, character_options, config_file):
@group_general_config
@group_character_options
def grant(general_config,
# Other (required)
bob_encrypting_key, bob_verifying_key, label, value, rate,
# Other
expiration, m, n,
# API Options
character_options, config_file
):
"""
Create and enact an access policy for some Bob.
"""
config_options = character_options.config_options
emitter = _setup_emitter(general_config)
bob_encrypting_key,
bob_verifying_key,
label,
value,
rate,
expiration,
m, n,
character_options,
config_file):
"""Create and enact an access policy for some Bob. """
# Setup
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)
# Input validation
@ -441,22 +461,10 @@ def grant(general_config,
@group_character_options
@option_config_file
@group_general_config
def revoke(general_config,
# Other (required)
bob_verifying_key, label,
# API Options
character_options, config_file
):
"""
Revoke a policy.
"""
emitter = _setup_emitter(general_config)
def revoke(general_config, bob_verifying_key, label, character_options, config_file):
"""Revoke a policy."""
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)
# Request
revoke_request = {'label': label, 'bob_verifying_key': bob_verifying_key}
return ALICE.controller.revoke(request=revoke_request)
@ -466,31 +474,10 @@ def revoke(general_config,
@group_character_options
@option_config_file
@group_general_config
def decrypt(general_config,
# Other (required)
label, message_kit,
# API Options
character_options, config_file
):
"""
Decrypt data encrypted under an Alice's policy public key.
"""
emitter = _setup_emitter(general_config)
def decrypt(general_config, label, message_kit, character_options, config_file):
"""Decrypt data encrypted under an Alice's policy public key."""
emitter = setup_emitter(general_config)
ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
# Request
request_data = {'label': label, 'message_kit': message_kit}
response = ALICE.controller.decrypt(request=request_data)
return response
def _setup_emitter(general_config):
# Banner
emitter = general_config.emitter
emitter.clear()
emitter.banner(ALICE_BANNER)
return emitter

View File

@ -1,6 +1,6 @@
import click
from nucypher.characters.banners import BOB_BANNER
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.characters.control.interfaces import BobInterface
from nucypher.cli.actions.auth import get_nucypher_password
from nucypher.cli.actions.config import (
@ -9,17 +9,32 @@ from nucypher.cli.actions.config import (
handle_missing_configuration_file
)
from nucypher.cli.actions.select import select_client_account
from nucypher.cli.actions.utils import make_cli_character
from nucypher.cli.actions.utils import make_cli_character, setup_emitter
from nucypher.cli.commands.deploy import option_gas_strategy
from nucypher.cli.config import group_general_config
from nucypher.cli.options import (group_options, option_checksum_address, option_config_file, option_config_root,
option_controller_port, option_dev, option_discovery_port, option_dry_run,
option_federated_only, option_force, option_middleware, option_min_stake,
option_network, option_provider_uri, option_registry_filepath, option_signer_uri,
option_teacher_uri)
from nucypher.cli.options import (
group_options,
option_checksum_address,
option_config_file,
option_config_root,
option_controller_port,
option_dev,
option_discovery_port,
option_dry_run,
option_federated_only,
option_force,
option_middleware,
option_min_stake,
option_network,
option_provider_uri,
option_registry_filepath,
option_signer_uri,
option_teacher_uri
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.config.characters import BobConfiguration
from nucypher.crypto.powers import DecryptingPower
from nucypher.network.middleware import RestMiddleware
from nucypher.utilities.sandbox.constants import TEMPORARY_DOMAIN
@ -27,8 +42,17 @@ class BobConfigOptions:
__option_name__ = 'config_options'
def __init__(self, provider_uri, network, registry_filepath, checksum_address, discovery_port,
dev, middleware, federated_only, gas_strategy, signer_uri):
def __init__(self,
provider_uri: str,
network: str,
registry_filepath: str,
checksum_address: str,
discovery_port: int,
dev: bool,
middleware: RestMiddleware,
federated_only: bool,
gas_strategy: str,
signer_uri: str):
self.provider_uri = provider_uri
self.signer_uri = signer_uri
@ -41,7 +65,7 @@ class BobConfigOptions:
self.middleware = middleware
self.federated_only = federated_only
def create_config(self, emitter, config_file):
def create_config(self, emitter: StdoutEmitter, config_file: str) -> BobConfiguration:
if self.dev:
return BobConfiguration(
emitter=emitter,
@ -71,7 +95,7 @@ class BobConfigOptions:
character_config_class=BobConfiguration,
config_file=config_file)
def generate_config(self, emitter, config_root):
def generate_config(self, emitter: StdoutEmitter, config_root: str) -> BobConfiguration:
checksum_address = self.checksum_address
if not checksum_address and not self.federated_only:
@ -115,21 +139,20 @@ group_config_options = group_options(
dev=option_dev,
middleware=option_middleware,
federated_only=option_federated_only
)
)
class BobCharacterOptions:
__option_name__ = 'character_options'
def __init__(self, config_options, teacher_uri, min_stake):
def __init__(self, config_options: BobConfigOptions, teacher_uri: str, min_stake: int):
self.config_options = config_options
self.teacher_uri = teacher_uri
self.min_stake = min_stake
def create_character(self, emitter, config_file):
config = self.config_options.create_config(emitter, config_file)
return make_cli_character(character_config=config,
emitter=emitter,
unlock_keyring=not self.config_options.dev,
@ -147,10 +170,7 @@ group_character_options = group_options(
@click.group()
def bob():
"""
"Bob the Data Recipient" management commands.
"""
pass
""""Bob the Data Recipient" management commands."""
@bob.command()
@ -159,10 +179,8 @@ def bob():
@option_config_root
@group_general_config
def init(general_config, config_options, config_root):
"""
Create a brand new persistent Bob.
"""
emitter = _setup_emitter(general_config)
"""Create a brand new persistent Bob."""
emitter = setup_emitter(general_config)
if not config_root:
config_root = general_config.config_root
new_bob_config = config_options.generate_config(emitter, config_root)
@ -176,11 +194,10 @@ def init(general_config, config_options, config_root):
@option_dry_run
@group_general_config
def run(general_config, character_options, config_file, controller_port, dry_run):
"""
Start Bob's controller.
"""
emitter = _setup_emitter(general_config)
"""Start Bob's controller."""
# Setup
emitter = setup_emitter(general_config)
BOB = character_options.create_character(emitter, config_file)
# RPC
@ -205,10 +222,8 @@ def run(general_config, character_options, config_file, controller_port, dry_run
@group_config_options
@group_general_config
def config(general_config, config_options, config_file):
"""
View and optionally update existing Bob's configuration.
"""
emitter = _setup_emitter(general_config)
"""View and optionally update existing Bob's configuration."""
emitter = setup_emitter(general_config)
bob_config = config_options.create_config(emitter, config_file)
filepath = config_file or bob_config.config_file_location
emitter.echo(f"Bob Configuration {filepath} \n {'='*55}")
@ -224,19 +239,12 @@ def config(general_config, config_options, config_file):
@option_force
@group_general_config
def destroy(general_config, config_options, config_file, force):
"""
Delete existing Bob's configuration.
"""
emitter = _setup_emitter(general_config)
# Validate
"""Delete existing Bob's configuration."""
emitter = setup_emitter(general_config)
if config_options.dev:
message = "'nucypher bob destroy' cannot be used in --dev mode"
raise click.BadOptionUsage(option_name='--dev', message=message)
bob_config = config_options.create_config(emitter, config_file)
# Request
return destroy_configuration(emitter, character_config=bob_config, force=force)
@ -246,10 +254,8 @@ def destroy(general_config, config_options, config_file, force):
@BobInterface.connect_cli('public_keys')
@group_general_config
def public_keys(general_config, character_options, config_file):
"""
Obtain Bob's public verification and encryption keys.
"""
emitter = _setup_emitter(general_config)
"""Obtain Bob's public verification and encryption keys."""
emitter = setup_emitter(general_config)
BOB = character_options.create_character(emitter, config_file)
response = BOB.controller.public_keys()
return response
@ -260,13 +266,17 @@ def public_keys(general_config, character_options, config_file):
@option_config_file
@BobInterface.connect_cli('retrieve')
@group_general_config
def retrieve(general_config, character_options, config_file,
label, policy_encrypting_key, alice_verifying_key, message_kit):
"""
Obtain plaintext from encrypted data, if access was granted.
"""
emitter = _setup_emitter(general_config)
def retrieve(general_config,
character_options,
config_file,
label,
policy_encrypting_key,
alice_verifying_key,
message_kit):
"""Obtain plaintext from encrypted data, if access was granted."""
# Setup
emitter = setup_emitter(general_config)
BOB = character_options.create_character(emitter, config_file)
# Validate
@ -285,12 +295,3 @@ def retrieve(general_config, character_options, config_file,
response = BOB.controller.retrieve(request=bob_request_data)
return response
def _setup_emitter(general_config):
# Banner
emitter = general_config.emitter
emitter.clear()
emitter.banner(BOB_BANNER)
return emitter

View File

@ -20,44 +20,86 @@ import json
import click
import os
from constant_sorrow import constants
from constant_sorrow.constants import (
FULL
)
from constant_sorrow.constants import FULL
from typing import Tuple
from nucypher.blockchain.eth.actors import ContractAdministrator, Trustee
from nucypher.blockchain.eth.agents import ContractAgency, MultiSigAgent, NucypherTokenAgent
from nucypher.blockchain.eth.constants import STAKING_ESCROW_CONTRACT_NAME
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.blockchain.eth.registry import (BaseContractRegistry, GithubRegistrySource, InMemoryContractRegistry,
RegistrySourceManager)
from nucypher.blockchain.eth.registry import (
BaseContractRegistry,
GithubRegistrySource,
InMemoryContractRegistry,
RegistrySourceManager
)
from nucypher.blockchain.eth.signers import Signer
from nucypher.blockchain.eth.token import NU
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import get_client_password
from nucypher.cli.actions.confirm import confirm_deployment
from nucypher.cli.actions.select import select_client_account
from nucypher.cli.actions.utils import establish_deployer_registry
from nucypher.cli.actions.utils import (_pre_launch_warnings, ensure_config_root, establish_deployer_registry,
initialize_deployer_interface)
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import CANNOT_OVERWRITE_REGISTRY, CONFIRM_BEGIN_UPGRADE, \
CONFIRM_BUILD_RETARGET_TRANSACTION, CONFIRM_LOCAL_REGISTRY_DESTRUCTION, CONFIRM_MANUAL_REGISTRY_DOWNLOAD, \
CONFIRM_NETWORK_ACTIVATION, CONFIRM_RETARGET, CONFIRM_SELECTED_ACCOUNT, CONFIRM_TOKEN_TRANSFER, \
CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY, CONTRACT_IS_NOT_OWNABLE, DEPLOYER_ADDRESS_ZERO_ETH, DEPLOYER_BALANCE, \
DISPLAY_SENDER_TOKEN_BALANCE_BEFORE_TRANSFER, ETHERSCAN_FLAG_DISABLED_WARNING, ETHERSCAN_FLAG_ENABLED_WARNING, \
EXISTING_REGISTRY_FOR_DOMAIN, MINIMUM_POLICY_RATE_EXCEEDED_WARNING, NO_HARDWARE_WALLET_WARNING, \
PROMPT_FOR_ALLOCATION_DATA_FILEPATH, PROMPT_NEW_DEFAULT_VALUE_FOR_RANGE, PROMPT_NEW_MAXIMUM_RANGE_VALUE, \
PROMPT_NEW_MIN_RANGE_VALUE, PROMPT_NEW_OWNER_ADDRESS, PROMPT_RECIPIENT_CHECKSUM_ADDRESS, PROMPT_TOKEN_VALUE, \
REGISTRY_NOT_AVAILABLE, SELECT_DEPLOYER_ACCOUNT, SUCCESSFUL_REGISTRY_CREATION, SUCCESSFUL_REGISTRY_DOWNLOAD, \
SUCCESSFUL_RETARGET, SUCCESSFUL_RETARGET_TX_BUILT, SUCCESSFUL_SAVE_BATCH_DEPOSIT_RECEIPTS, \
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS, SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL, SUCCESSFUL_UPGRADE, UNKNOWN_CONTRACT_NAME
from nucypher.cli.options import (group_options, option_config_root, option_contract_name, option_etherscan,
option_force, option_hw_wallet, option_poa, option_provider_uri, option_signer_uri)
from nucypher.cli.painting.deployment import paint_contract_deployment, paint_deployer_contract_inspection, \
paint_deployment_delay, paint_staged_deployment
from nucypher.cli.literature import (
CANNOT_OVERWRITE_REGISTRY,
CONFIRM_BEGIN_UPGRADE,
CONFIRM_BUILD_RETARGET_TRANSACTION,
CONFIRM_LOCAL_REGISTRY_DESTRUCTION,
CONFIRM_MANUAL_REGISTRY_DOWNLOAD,
CONFIRM_NETWORK_ACTIVATION,
CONFIRM_RETARGET,
CONFIRM_SELECTED_ACCOUNT,
CONFIRM_TOKEN_TRANSFER,
CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY,
CONTRACT_IS_NOT_OWNABLE,
DEPLOYER_ADDRESS_ZERO_ETH,
DEPLOYER_BALANCE,
DISPLAY_SENDER_TOKEN_BALANCE_BEFORE_TRANSFER,
EXISTING_REGISTRY_FOR_DOMAIN,
MINIMUM_POLICY_RATE_EXCEEDED_WARNING,
PROMPT_FOR_ALLOCATION_DATA_FILEPATH,
PROMPT_NEW_DEFAULT_VALUE_FOR_RANGE,
PROMPT_NEW_MAXIMUM_RANGE_VALUE,
PROMPT_NEW_MIN_RANGE_VALUE,
PROMPT_NEW_OWNER_ADDRESS,
PROMPT_RECIPIENT_CHECKSUM_ADDRESS,
PROMPT_TOKEN_VALUE,
REGISTRY_NOT_AVAILABLE,
SELECT_DEPLOYER_ACCOUNT,
SUCCESSFUL_REGISTRY_CREATION,
SUCCESSFUL_REGISTRY_DOWNLOAD,
SUCCESSFUL_RETARGET,
SUCCESSFUL_RETARGET_TX_BUILT,
SUCCESSFUL_SAVE_BATCH_DEPOSIT_RECEIPTS,
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS,
SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL,
SUCCESSFUL_UPGRADE,
UNKNOWN_CONTRACT_NAME
)
from nucypher.cli.options import (
group_options,
option_config_root,
option_contract_name,
option_etherscan,
option_force,
option_hw_wallet,
option_poa,
option_provider_uri,
option_signer_uri
)
from nucypher.cli.painting.deployment import (
paint_contract_deployment,
paint_deployer_contract_inspection,
paint_deployment_delay,
paint_staged_deployment
)
from nucypher.cli.painting.help import echo_solidity_version
from nucypher.cli.painting.multisig import paint_multisig_proposed_transaction
from nucypher.cli.painting.transactions import paint_receipt_summary
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, EXISTING_READABLE_FILE, WEI
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
option_deployer_address = click.option('--deployer-address', help="Deployer's checksum address", type=EIP55_CHECKSUM_ADDRESS)
option_registry_infile = click.option('--registry-infile', help="Input path for contract registry file", type=EXISTING_READABLE_FILE)
@ -70,60 +112,26 @@ option_ignore_deployed = click.option('--ignore-deployed', help="Ignore already
option_ignore_solidity_version = click.option('--ignore-solidity-check', help="Ignore solidity version compatibility check", is_flag=True)
def _pre_launch_warnings(emitter, etherscan, hw_wallet):
if not hw_wallet:
emitter.echo(NO_HARDWARE_WALLET_WARNING, color='yellow')
if etherscan:
emitter.echo(ETHERSCAN_FLAG_ENABLED_WARNING, color='yellow')
else:
emitter.echo(ETHERSCAN_FLAG_DISABLED_WARNING, color='yellow')
def _initialize_blockchain(poa, provider_uri, emitter, ignore_solidity_check, gas_strategy=None):
if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
# Note: For test compatibility.
deployer_interface = BlockchainDeployerInterface(provider_uri=provider_uri,
poa=poa,
ignore_solidity_check=ignore_solidity_check,
gas_strategy=gas_strategy)
BlockchainInterfaceFactory.register_interface(interface=deployer_interface,
sync=False,
emitter=emitter)
else:
deployer_interface = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
deployer_interface.connect()
return deployer_interface
def _ensure_config_root(config_root):
# Ensure config root exists, because we need a default place to put output files.
config_root = config_root or DEFAULT_CONFIG_ROOT
if not os.path.exists(config_root):
os.makedirs(config_root)
class ActorOptions:
__option_name__ = 'actor_options'
def __init__(self,
provider_uri,
deployer_address,
contract_name,
registry_infile,
registry_outfile,
hw_wallet,
dev,
force,
poa,
config_root,
etherscan,
provider_uri: str,
deployer_address: str,
contract_name: str,
registry_infile: str,
registry_outfile: str,
hw_wallet: bool,
dev: bool,
force: bool,
poa: bool,
config_root: str,
etherscan: bool,
se_test_mode,
ignore_solidity_check,
gas_strategy,
signer_uri
gas_strategy: str,
signer_uri: str
):
self.provider_uri = provider_uri
@ -142,14 +150,17 @@ class ActorOptions:
self.se_test_mode = se_test_mode
self.ignore_solidity_check = ignore_solidity_check
def create_actor(self, emitter, is_multisig: bool = False):
def create_actor(self,
emitter: StdoutEmitter,
is_multisig: bool = False
) -> Tuple[ContractAdministrator, str, BlockchainInterface, BaseContractRegistry]:
_ensure_config_root(self.config_root)
deployer_interface = _initialize_blockchain(poa=self.poa,
provider_uri=self.provider_uri,
emitter=emitter,
ignore_solidity_check=self.ignore_solidity_check,
gas_strategy=self.gas_strategy)
ensure_config_root(self.config_root)
deployer_interface = initialize_deployer_interface(poa=self.poa,
provider_uri=self.provider_uri,
emitter=emitter,
ignore_solidity_check=self.ignore_solidity_check,
gas_strategy=self.gas_strategy)
# Warnings
_pre_launch_warnings(emitter, self.etherscan, self.hw_wallet)
@ -219,7 +230,7 @@ group_actor_options = group_options(
config_root=option_config_root,
etherscan=option_etherscan,
ignore_solidity_check=option_ignore_solidity_version
)
)
@click.group()
@ -230,10 +241,7 @@ group_actor_options = group_options(
expose_value=False,
is_eager=True)
def deploy():
"""
Manage contract and registry deployment.
"""
pass
"""Manage contract and registry deployment."""
@deploy.command(name='download-registry')
@ -243,13 +251,11 @@ def deploy():
@option_network
@option_force
def download_registry(general_config, config_root, registry_outfile, network, force):
"""
Download the latest registry.
"""
# Init
emitter = general_config.emitter
_ensure_config_root(config_root)
"""Download the latest registry."""
# Setup
emitter = general_config.emitter
ensure_config_root(config_root)
github_source = GithubRegistrySource(network=network, registry_name=BaseContractRegistry.REGISTRY_NAME)
source_manager = RegistrySourceManager(sources=[github_source])
@ -279,23 +285,19 @@ def download_registry(general_config, config_root, registry_outfile, network, fo
@option_poa
@option_ignore_solidity_version
def inspect(general_config, provider_uri, config_root, registry_infile, deployer_address, poa, ignore_solidity_check):
"""
Echo owner information and bare contract metadata.
"""
# Init
"""Echo owner information and bare contract metadata."""
emitter = general_config.emitter
_ensure_config_root(config_root)
_initialize_blockchain(poa=poa,
provider_uri=provider_uri,
emitter=emitter,
ignore_solidity_check=ignore_solidity_check)
ensure_config_root(config_root)
initialize_deployer_interface(poa=poa,
provider_uri=provider_uri,
emitter=emitter,
ignore_solidity_check=ignore_solidity_check)
local_registry = establish_deployer_registry(emitter=emitter,
registry_infile=registry_infile,
download_registry=not bool(registry_infile))
paint_deployer_contract_inspection(emitter=emitter,
registry=local_registry,
deployer_address=deployer_address)
return paint_deployer_contract_inspection(emitter=emitter,
registry=local_registry,
deployer_address=deployer_address)
@deploy.command()
@ -369,15 +371,12 @@ def upgrade(general_config, actor_options, retarget, target_address, ignore_depl
@group_general_config
@group_actor_options
def rollback(general_config, actor_options):
"""
Rollback a proxy contract's target.
"""
"""Rollback a proxy contract's target."""
emitter = general_config.emitter
ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter)
if not actor_options.contract_name:
raise click.BadArgumentUsage(message="--contract-name is required when using --rollback")
ADMINISTRATOR.rollback_contract(contract_name=actor_options.contract_name)
return ADMINISTRATOR.rollback_contract(contract_name=actor_options.contract_name)
@deploy.command()
@ -396,10 +395,7 @@ def rollback(general_config, actor_options):
)
@click.option('--activate', help="Activate a contract that is in idle mode", is_flag=True)
def contracts(general_config, actor_options, mode, activate, gas, ignore_deployed, confirmations, parameters):
"""
Compile and deploy contracts.
"""
# Init
"""Compile and deploy contracts."""
emitter = general_config.emitter
ADMINISTRATOR, _, deployer_interface, local_registry = actor_options.create_actor(emitter)
@ -507,21 +503,16 @@ def contracts(general_config, actor_options, mode, activate, gas, ignore_deploye
@click.option('--allocation-infile', help="Input path for token allocation JSON file", type=EXISTING_READABLE_FILE)
@option_gas
def allocations(general_config, actor_options, allocation_infile, gas):
"""
Deposit stake allocations in batches
"""
"""Deposit stake allocations in batches"""
emitter = general_config.emitter
ADMINISTRATOR, _, deployer_interface, local_registry = actor_options.create_actor(emitter)
if not allocation_infile:
allocation_infile = click.prompt(PROMPT_FOR_ALLOCATION_DATA_FILEPATH)
receipts = ADMINISTRATOR.batch_deposits(allocation_data_filepath=allocation_infile,
emitter=emitter,
gas_limit=gas,
interactive=not actor_options.force)
receipts_filepath = ADMINISTRATOR.save_deployment_receipts(receipts=receipts,
filename_prefix='batch_deposits')
receipts_filepath = ADMINISTRATOR.save_deployment_receipts(receipts=receipts, filename_prefix='batch_deposits')
if emitter:
emitter.echo(SUCCESSFUL_SAVE_BATCH_DEPOSIT_RECEIPTS.format(receipts_filepath=receipts_filepath), color='blue', bold=True)
@ -532,9 +523,8 @@ def allocations(general_config, actor_options, allocation_infile, gas):
@option_target_address
@click.option('--value', help="Amount of tokens to transfer in the smallest denomination", type=click.INT)
def transfer_tokens(general_config, actor_options, target_address, value):
"""
Transfer tokens from contract's owner address to another address
"""
"""Transfer tokens from contract's owner address to another address"""
emitter = general_config.emitter
ADMINISTRATOR, deployer_address, _, local_registry = actor_options.create_actor(emitter)
@ -552,7 +542,7 @@ def transfer_tokens(general_config, actor_options, target_address, value):
target_address=target_address)
click.confirm(confirmation, abort=True)
receipt = token_agent.transfer(amount=int(value), sender_address=deployer_address, target_address=target_address)
paint_receipt_summary(emitter=emitter, receipt=receipt)
return paint_receipt_summary(emitter=emitter, receipt=receipt)
@deploy.command("transfer-ownership")
@ -607,13 +597,11 @@ def set_range(general_config, actor_options, minimum, default, maximum):
"""
emitter = general_config.emitter
ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter)
if not minimum:
minimum = click.prompt(PROMPT_NEW_MIN_RANGE_VALUE, type=click.IntRange(min=0))
if not default:
default = click.prompt(PROMPT_NEW_DEFAULT_VALUE_FOR_RANGE, type=click.IntRange(min=minimum))
if not maximum:
maximum = click.prompt(PROMPT_NEW_MAXIMUM_RANGE_VALUE, type=click.IntRange(min=default))
ADMINISTRATOR.set_min_reward_rate_range(minimum=minimum, default=default, maximum=maximum)
emitter.echo(MINIMUM_POLICY_RATE_EXCEEDED_WARNING.format(minimum=minimum, maximum=maximum, default=default))

View File

@ -1,9 +1,27 @@
"""
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/>.
"""
import click
from umbral.keys import UmbralPublicKey
from nucypher.characters.banners import ENRICO_BANNER
from nucypher.characters.control.interfaces import EnricoInterface
from nucypher.characters.lawful import Enrico
from nucypher.cli.actions.utils import setup_emitter
from nucypher.cli.config import group_general_config
from nucypher.cli.options import option_dry_run, option_policy_encrypting_key
from nucypher.cli.types import NETWORK_PORT
@ -11,10 +29,7 @@ from nucypher.cli.types import NETWORK_PORT
@click.group()
def enrico():
"""
"Enrico the Encryptor" management commands.
"""
pass
""""Enrico the Encryptor" management commands."""
@enrico.command()
@ -23,15 +38,11 @@ def enrico():
@click.option('--http-port', help="The host port to run Enrico HTTP services on", type=NETWORK_PORT)
@group_general_config
def run(general_config, policy_encrypting_key, dry_run, http_port):
"""
Start Enrico's controller.
"""
### Setup ###
emitter = _setup_emitter(general_config, policy_encrypting_key)
"""Start Enrico's controller."""
# Setup
emitter = setup_emitter(general_config, policy_encrypting_key)
ENRICO = _create_enrico(emitter, policy_encrypting_key)
#############
# RPC
if general_config.json_ipc:
@ -49,33 +60,16 @@ def run(general_config, policy_encrypting_key, dry_run, http_port):
@EnricoInterface.connect_cli('encrypt_message')
@group_general_config
def encrypt(general_config, policy_encrypting_key, message):
"""
Encrypt a message under a given policy public key.
"""
### Setup ###
emitter = _setup_emitter(general_config, policy_encrypting_key)
"""Encrypt a message under a given policy public key."""
emitter = setup_emitter(general_config=general_config, banner=policy_encrypting_key)
ENRICO = _create_enrico(emitter, policy_encrypting_key)
#############
# Request
encryption_request = {'message': message}
response = ENRICO.controller.encrypt_message(request=encryption_request)
return response
def _setup_emitter(general_config, policy_encrypting_key):
emitter = general_config.emitter
emitter.clear()
emitter.banner(ENRICO_BANNER.format(policy_encrypting_key))
return emitter
def _create_enrico(emitter, policy_encrypting_key):
def _create_enrico(emitter, policy_encrypting_key) -> Enrico:
policy_encrypting_key = UmbralPublicKey.from_bytes(bytes.fromhex(policy_encrypting_key))
ENRICO = Enrico(policy_encrypting_key=policy_encrypting_key)
ENRICO.controller.emitter = emitter
return ENRICO

View File

@ -1,13 +1,39 @@
"""
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/>.
"""
import click
import os
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from nucypher.characters.banners import FELIX_BANNER
from nucypher.cli.actions.auth import get_client_password, get_nucypher_password, unlock_nucypher_keyring
from nucypher.cli.actions.auth import (
get_client_password,
get_nucypher_password,
unlock_nucypher_keyring
)
from nucypher.cli.actions.config import destroy_configuration, get_provider_process, handle_missing_configuration_file
from nucypher.cli.actions.utils import setup_emitter
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import CONFIRM_OVERWRITE_DATABASE, FELIX_RUN_MESSAGE, SUCCESSFUL_DATABASE_CREATION, \
from nucypher.cli.literature import (
CONFIRM_OVERWRITE_DATABASE,
FELIX_RUN_MESSAGE,
SUCCESSFUL_DATABASE_CREATION,
SUCCESSFUL_DATABASE_DESTRUCTION
)
from nucypher.cli.options import (
group_options,
option_checksum_address,
@ -39,9 +65,17 @@ class FelixConfigOptions:
__option_name__ = 'config_options'
def __init__(
self, geth, dev, network, provider_uri, host,
db_filepath, checksum_address, registry_filepath, poa, port):
def __init__(self,
geth,
dev,
network,
provider_uri,
host,
db_filepath,
checksum_address,
registry_filepath,
poa,
port):
eth_node = NO_BLOCKCHAIN_CONNECTION
if geth:
@ -105,7 +139,7 @@ group_config_options = group_options(
registry_filepath=option_registry_filepath,
poa=option_poa,
port=option_port,
)
)
class FelixCharacterOptions:
@ -150,15 +184,12 @@ group_character_options = group_options(
teacher_uri=option_teacher_uri,
min_stake=option_min_stake,
middleware=option_middleware,
)
)
@click.group()
def felix():
"""
"Felix the Faucet" management commands.
"""
pass
""""Felix the Faucet" management commands."""
@felix.command()
@ -167,14 +198,10 @@ def felix():
@option_discovery_port(default=FelixConfiguration.DEFAULT_LEARNER_PORT)
@group_config_options
def init(general_config, config_options, config_root, discovery_port):
"""
Create a brand-new Felix.
"""
emitter = _setup_emitter(general_config, config_options.checksum_address)
"""Create a brand-new Felix."""
emitter = setup_emitter(general_config=general_config, banner=config_options.checksum_address)
if not config_root: # Flag
config_root = DEFAULT_CONFIG_ROOT # Envvar or init-only default
try:
new_felix_config = config_options.generate_config(config_root, discovery_port)
except Exception as e:
@ -183,8 +210,6 @@ def init(general_config, config_options, config_root, discovery_port):
else:
emitter.echo(str(e), color='red', bold=True)
raise click.Abort
# Paint Help
paint_new_installation_help(emitter, new_configuration=new_felix_config)
@ -194,10 +219,8 @@ def init(general_config, config_options, config_root, discovery_port):
@option_force
@group_general_config
def destroy(general_config, config_options, config_file, force):
"""
Destroy Felix Configuration.
"""
emitter = _setup_emitter(general_config, config_options.checksum_address)
"""Destroy Felix Configuration."""
emitter = setup_emitter(general_config, config_options.checksum_address)
felix_config = config_options.create_config(emitter, config_file)
destroy_configuration(emitter, character_config=felix_config, force=force)
@ -208,19 +231,14 @@ def destroy(general_config, config_options, config_file, force):
@option_force
@group_general_config
def createdb(general_config, character_options, config_file, force):
"""
Create Felix DB.
"""
emitter = _setup_emitter(general_config, character_options.config_options.checksum_address)
"""Create Felix DB."""
emitter = setup_emitter(general_config, character_options.config_options.checksum_address)
FELIX = character_options.create_character(emitter, config_file, general_config.debug)
if os.path.isfile(FELIX.db_filepath):
if not force:
click.confirm(CONFIRM_OVERWRITE_DATABASE, abort=True)
os.remove(FELIX.db_filepath)
emitter.echo(SUCCESSFUL_DATABASE_DESTRUCTION.format(path=FELIX.db_filepath))
FELIX.create_tables()
emitter.echo(SUCCESSFUL_DATABASE_CREATION.format(path=FELIX.db_filepath), color='green')
@ -230,13 +248,9 @@ def createdb(general_config, character_options, config_file, force):
@option_config_file
@group_general_config
def view(general_config, character_options, config_file):
"""
View Felix token balance.
"""
emitter = _setup_emitter(general_config, character_options.config_options.checksum_address)
"""View Felix token balance."""
emitter = setup_emitter(general_config, character_options.config_options.checksum_address)
FELIX = character_options.create_character(emitter, config_file, general_config.debug)
token_balance = FELIX.token_balance
eth_balance = FELIX.eth_balance
emitter.echo(f"""
@ -251,13 +265,9 @@ def view(general_config, character_options, config_file):
@option_config_file
@group_general_config
def accounts(general_config, character_options, config_file):
"""
View Felix known accounts.
"""
emitter = _setup_emitter(general_config, character_options.config_options.checksum_address)
"""View Felix known accounts."""
emitter = setup_emitter(general_config, character_options.config_options.checksum_address)
FELIX = character_options.create_character(emitter, config_file, general_config.debug)
accounts = FELIX.blockchain.client.accounts
for account in accounts:
emitter.echo(account)
@ -269,13 +279,9 @@ def accounts(general_config, character_options, config_file):
@option_dry_run
@group_general_config
def run(general_config, character_options, config_file, dry_run):
"""
Run Felix service.
"""
emitter = _setup_emitter(general_config, character_options.config_options.checksum_address)
"""Run Felix services."""
emitter = setup_emitter(general_config, character_options.config_options.checksum_address)
FELIX = character_options.create_character(emitter, config_file, general_config.debug)
host = character_options.config_options.host
port = character_options.config_options.port
emitter.message(FELIX_RUN_MESSAGE.format(host=host, port=port))
@ -284,13 +290,3 @@ def run(general_config, character_options, config_file, dry_run):
web_services=not dry_run,
distribution=True,
crash_on_error=general_config.debug)
def _setup_emitter(general_config, checksum_address):
emitter = general_config.emitter
# Intro
emitter.clear()
emitter.banner(FELIX_BANNER.format(checksum_address or ''))
return emitter

View File

@ -17,11 +17,10 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import click
import os
from nucypher.blockchain.eth.actors import Executive, Trustee
from nucypher.blockchain.eth.agents import ContractAgency, MultiSigAgent, NucypherTokenAgent
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.multisig import Authorization, Proposal
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, LocalContractRegistry
from nucypher.blockchain.eth.signers import ClefSigner
@ -31,44 +30,29 @@ from nucypher.cli.actions.select import select_client_account
from nucypher.cli.actions.utils import get_registry
from nucypher.cli.commands.stake import option_signer_uri
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import CONFIRM_EXECUTE_MULTISIG_TRANSACTION, MULTISIG_SIGNATURE_RECEIVED, \
PROMPT_CONFIRM_MULTISIG_SIGNATURE, PROMPT_FOR_RAW_SIGNATURE, PROMPT_NEW_MULTISIG_THRESHOLD, \
SUCCESSFUL_MULTISIG_AUTHORIZATION, SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL
from nucypher.cli.options import (group_options, option_checksum_address, option_geth, option_hw_wallet, option_light,
option_network, option_poa, option_provider_uri, option_registry_filepath)
from nucypher.cli.literature import (
CONFIRM_EXECUTE_MULTISIG_TRANSACTION,
MULTISIG_SIGNATURE_RECEIVED,
PROMPT_CONFIRM_MULTISIG_SIGNATURE,
PROMPT_FOR_RAW_SIGNATURE,
PROMPT_NEW_MULTISIG_THRESHOLD,
SUCCESSFUL_MULTISIG_AUTHORIZATION,
SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL
)
from nucypher.cli.options import (
group_options,
option_checksum_address,
option_geth,
option_hw_wallet,
option_light,
option_network,
option_poa,
option_provider_uri,
option_registry_filepath
)
from nucypher.cli.painting.multisig import paint_multisig_contract_info, paint_multisig_proposed_transaction
from nucypher.cli.painting.transactions import paint_receipt_summary
from nucypher.cli.types import EXISTING_READABLE_FILE
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
def _setup_emitter(general_config):
emitter = general_config.emitter
return emitter
def _initialize_blockchain(poa, provider_uri, emitter, gas_strategy=None):
if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
# Note: For test compatibility.
deployer_interface = BlockchainDeployerInterface(provider_uri=provider_uri,
poa=poa,
gas_strategy=gas_strategy)
BlockchainInterfaceFactory.register_interface(interface=deployer_interface,
sync=False,
emitter=emitter)
else:
deployer_interface = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
deployer_interface.connect()
return deployer_interface
def _ensure_config_root(config_root):
# Ensure config root exists, because we need a default place to put output files.
config_root = config_root or DEFAULT_CONFIG_ROOT
if not os.path.exists(config_root):
os.makedirs(config_root)
# TODO: Same option group in nucypher status (called RegistryOptions). Make something generic
@ -179,29 +163,20 @@ group_multisig_options = group_options(
@click.group()
def multisig():
"""
Perform operations on NuCypher contracts via a MultiSig
"""
pass
"""Perform operations on NuCypher contracts via a MultiSig"""
@multisig.command()
@group_general_config
@group_blockchain_options
def inspect(general_config, blockchain_options):
"""
Show information of the MultiSig contract
"""
# Init
"""Show information of the MultiSig contract"""
emitter = general_config.emitter
_blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug)
registry = get_registry(network=blockchain_options.network)
multisig_agent = ContractAgency.get_agent(MultiSigAgent, registry=registry)
token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=registry)
paint_multisig_contract_info(emitter, multisig_agent, token_agent)
return
return paint_multisig_contract_info(emitter, multisig_agent, token_agent)
@multisig.command()
@ -209,9 +184,7 @@ def inspect(general_config, blockchain_options):
@group_blockchain_options
@group_multisig_options
def propose(general_config, blockchain_options, multisig_options):
"""
Create a proposal of MultiSig transaction
"""
"""Create a proposal of MultiSig transaction"""
# TODO: Extend this command to cover this list of proposals
# - Add new MultiSig owner
# - Remove MultiSig owner

View File

@ -20,7 +20,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import click
from web3 import Web3
from nucypher.blockchain.eth.actors import StakeHolder
from nucypher.blockchain.eth.constants import MAX_UINT16
from nucypher.blockchain.eth.events import EventRecord
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
@ -29,29 +28,68 @@ from nucypher.blockchain.eth.token import NU, StakeList
from nucypher.blockchain.eth.utils import datetime_at_period
from nucypher.cli.actions.auth import get_client_password
from nucypher.cli.actions.config import get_or_update_configuration, handle_missing_configuration_file
from nucypher.cli.actions.confirm import (confirm_enable_restaking, confirm_enable_restaking_lock,
confirm_enable_winding_down, confirm_large_stake, confirm_staged_stake)
from nucypher.cli.actions.confirm import (
confirm_enable_restaking,
confirm_enable_restaking_lock,
confirm_enable_winding_down,
confirm_large_stake,
confirm_staged_stake
)
from nucypher.cli.actions.select import handle_client_account_for_staking, select_stake
from nucypher.cli.actions.utils import setup_emitter
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import (BONDING_DETAILS, BONDING_RELEASE_INFO, COLLECTING_ETH_REWARD,
COLLECTING_PREALLOCATION_REWARD, COLLECTING_TOKEN_REWARD,
CONFIRM_BROADCAST_STAKE_DIVIDE, CONFIRM_DISABLE_RESTAKING,
CONFIRM_DISABLE_WIND_DOWN, CONFIRM_NEW_MIN_POLICY_RATE, CONFIRM_PROLONG,
CONFIRM_WORKER_AND_STAKER_ADDRESSES_ARE_EQUAL, DETACH_DETAILS,
PERIOD_ADVANCED_WARNING, PROMPT_PROLONG_VALUE, PROMPT_STAKER_MIN_POLICY_RATE,
PROMPT_STAKE_DIVIDE_VALUE, PROMPT_STAKE_EXTEND_VALUE, PROMPT_WORKER_ADDRESS,
SUCCESSFUL_DETACH_WORKER, SUCCESSFUL_DISABLE_RESTAKING,
SUCCESSFUL_DISABLE_WIND_DOWN, SUCCESSFUL_ENABLE_RESTAKE_LOCK,
SUCCESSFUL_ENABLE_RESTAKING, SUCCESSFUL_ENABLE_WIND_DOWN,
SUCCESSFUL_NEW_STAKEHOLDER_CONFIG, SUCCESSFUL_SET_MIN_POLICY_RATE,
SUCCESSFUL_STAKE_DIVIDE, SUCCESSFUL_STAKE_PROLONG, SUCCESSFUL_WORKER_BONDING)
from nucypher.cli.options import (group_options, option_config_file, option_config_root, option_event_name,
option_force, option_hw_wallet, option_light, option_network, option_poa,
option_provider_uri, option_registry_filepath, option_signer_uri,
option_staking_address)
from nucypher.cli.literature import (
BONDING_DETAILS,
BONDING_RELEASE_INFO,
COLLECTING_ETH_REWARD,
COLLECTING_PREALLOCATION_REWARD,
COLLECTING_TOKEN_REWARD,
CONFIRM_BROADCAST_STAKE_DIVIDE,
CONFIRM_DISABLE_RESTAKING, CONFIRM_DISABLE_WIND_DOWN,
CONFIRM_NEW_MIN_POLICY_RATE,
CONFIRM_PROLONG,
CONFIRM_WORKER_AND_STAKER_ADDRESSES_ARE_EQUAL,
DETACH_DETAILS,
PERIOD_ADVANCED_WARNING,
PROMPT_PROLONG_VALUE,
PROMPT_STAKER_MIN_POLICY_RATE,
PROMPT_STAKE_DIVIDE_VALUE,
PROMPT_STAKE_EXTEND_VALUE,
PROMPT_WORKER_ADDRESS,
SUCCESSFUL_DETACH_WORKER,
SUCCESSFUL_DISABLE_RESTAKING, SUCCESSFUL_DISABLE_WIND_DOWN,
SUCCESSFUL_ENABLE_RESTAKE_LOCK,
SUCCESSFUL_ENABLE_RESTAKING,
SUCCESSFUL_ENABLE_WIND_DOWN,
SUCCESSFUL_NEW_STAKEHOLDER_CONFIG,
SUCCESSFUL_SET_MIN_POLICY_RATE,
SUCCESSFUL_STAKE_DIVIDE,
SUCCESSFUL_STAKE_PROLONG,
SUCCESSFUL_WORKER_BONDING
)
from nucypher.cli.options import (
group_options,
option_config_file,
option_config_root,
option_event_name,
option_force,
option_hw_wallet,
option_light,
option_network,
option_poa,
option_provider_uri,
option_registry_filepath,
option_signer_uri,
option_staking_address
)
from nucypher.cli.painting.policies import paint_min_rate
from nucypher.cli.painting.staking import paint_staged_stake, paint_staged_stake_division, paint_stakes, \
paint_staking_accounts, paint_staking_confirmation
from nucypher.cli.painting.staking import (
paint_staged_stake,
paint_staged_stake_division,
paint_stakes,
paint_staking_accounts,
paint_staking_confirmation
)
from nucypher.cli.painting.status import paint_preallocation_status
from nucypher.cli.painting.transactions import paint_receipt_summary
from nucypher.cli.types import (
@ -66,14 +104,6 @@ option_lock_periods = click.option('--lock-periods', help="Duration of stake in
option_worker_address = click.option('--worker-address', help="Address to bond as an Ursula-Worker", type=EIP55_CHECKSUM_ADDRESS)
def _setup_emitter(general_config):
# Banner
emitter = general_config.emitter
emitter.banner(StakeHolder.banner)
return emitter
class StakeHolderConfigOptions:
__option_name__ = 'config_options'
@ -161,14 +191,9 @@ class StakerOptions:
def create_character(self, emitter, config_file, initial_address=None, *args, **kwargs):
stakeholder_config = self.config_options.create_config(emitter, config_file)
if initial_address is None:
initial_address = self.staking_address
return stakeholder_config.produce(
initial_address=initial_address,
*args, **kwargs
)
return stakeholder_config.produce(initial_address=initial_address, *args, **kwargs)
def get_blockchain(self):
return BlockchainInterfaceFactory.get_interface(provider_uri=self.config_options.provider_uri) # Eager connection
@ -253,10 +278,7 @@ group_transacting_staker_options = group_options(
@click.group()
def stake():
"""
Manage stakes and other staker-related operations.
"""
pass
"""Manage stakes and other staker-related operations."""
@stake.command(name='init-stakeholder')
@ -266,10 +288,11 @@ def stake():
@group_general_config
def init_stakeholder(general_config, config_root, force, config_options):
"""Create a new stakeholder configuration."""
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
new_stakeholder = config_options.generate_config(config_root)
filepath = new_stakeholder.to_configuration_file(override=force)
emitter.echo(SUCCESSFUL_NEW_STAKEHOLDER_CONFIG.format(filepath=filepath), color='green')
return # Exit
@stake.command()
@ -278,7 +301,7 @@ def init_stakeholder(general_config, config_root, force, config_options):
@group_config_options
def config(general_config, config_file, config_options):
"""View and optionally update existing StakeHolder's configuration."""
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
configuration_file_location = config_file or StakeHolderConfiguration.default_filepath()
emitter.echo(f"StakeHolder Configuration {configuration_file_location} \n {'='*55}")
return get_or_update_configuration(emitter=emitter,
@ -293,12 +316,11 @@ def config(general_config, config_file, config_options):
@click.option('--all', help="List all stakes, including inactive", is_flag=True)
@group_general_config
def list_stakes(general_config, staker_options, config_file, all):
"""
List active stakes for current stakeholder.
"""
emitter = _setup_emitter(general_config)
"""List active stakes for current stakeholder."""
emitter = setup_emitter(general_config)
STAKEHOLDER = staker_options.create_character(emitter, config_file)
paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER, paint_inactive=all)
return # Exit
@stake.command()
@ -306,12 +328,11 @@ def list_stakes(general_config, staker_options, config_file, all):
@option_config_file
@group_general_config
def accounts(general_config, staker_options, config_file):
"""
Show ETH and NU balances for stakeholder's accounts.
"""
emitter = _setup_emitter(general_config)
"""Show ETH and NU balances for stakeholder's accounts."""
emitter = setup_emitter(general_config)
STAKEHOLDER = staker_options.create_character(emitter, config_file)
paint_staking_accounts(emitter=emitter, wallet=STAKEHOLDER.wallet, registry=STAKEHOLDER.registry)
return # Exit
@stake.command('bond-worker')
@ -321,14 +342,11 @@ def accounts(general_config, staker_options, config_file):
@group_general_config
@option_worker_address
def bond_worker(general_config, transacting_staker_options, config_file, force, worker_address):
"""
Bond a worker to a staker.
"""
emitter = _setup_emitter(general_config)
"""Bond a worker to a staker."""
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
economics = STAKEHOLDER.economics
client_account, staking_address = handle_client_account_for_staking(
@ -376,6 +394,7 @@ def bond_worker(general_config, transacting_staker_options, config_file, force,
transaction_type='bond_worker')
emitter.echo(BONDING_DETAILS.format(current_period=current_period, bonded_date=bonded_date), color='green')
emitter.echo(BONDING_RELEASE_INFO.format(release_period=release_period, release_date=release_date), color='green')
return # Exit
@stake.command('unbond-worker')
@ -387,7 +406,7 @@ def unbond_worker(general_config, transacting_staker_options, config_file, force
"""
Unbond worker currently bonded to a staker.
"""
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
@ -402,11 +421,8 @@ def unbond_worker(general_config, transacting_staker_options, config_file, force
force=force)
# TODO: Check preconditions (e.g., minWorkerPeriods)
worker_address = STAKEHOLDER.staking_agent.get_worker_from_staker(staking_address)
password = transacting_staker_options.get_password(blockchain, client_account)
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
receipt = STAKEHOLDER.unbond_worker()
@ -421,6 +437,7 @@ def unbond_worker(general_config, transacting_staker_options, config_file, force
chain_name=blockchain.client.chain_name,
transaction_type='unbond_worker')
emitter.echo(DETACH_DETAILS.format(current_period=current_period, bonded_date=bonded_date), color='green')
return # Exit
@stake.command()
@ -431,14 +448,12 @@ def unbond_worker(general_config, transacting_staker_options, config_file, force
@option_lock_periods
@group_general_config
def create(general_config, transacting_staker_options, config_file, force, value, lock_periods):
"""
Initialize a new stake.
"""
emitter = _setup_emitter(general_config)
"""Initialize a new stake."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
economics = STAKEHOLDER.economics
client_account, staking_address = handle_client_account_for_staking(
@ -509,7 +524,7 @@ def create(general_config, transacting_staker_options, config_file, force, value
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
new_stake = STAKEHOLDER.initialize_stake(amount=value, lock_periods=lock_periods)
paint_staking_confirmation(emitter=emitter, staker=STAKEHOLDER, new_stake=new_stake)
return paint_staking_confirmation(emitter=emitter, staker=STAKEHOLDER, new_stake=new_stake)
@stake.command()
@ -520,11 +535,10 @@ def create(general_config, transacting_staker_options, config_file, force, value
@option_force
@group_general_config
def restake(general_config, transacting_staker_options, config_file, enable, lock_until, force):
"""
Manage re-staking with --enable or --disable.
"""
emitter = _setup_emitter(general_config)
"""Manage re-staking with --enable or --disable."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
@ -537,7 +551,6 @@ def restake(general_config, transacting_staker_options, config_file, enable, loc
# Authenticate
password = transacting_staker_options.get_password(blockchain, client_account)
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
# Inner Exclusive Switch
@ -559,6 +572,7 @@ def restake(general_config, transacting_staker_options, config_file, enable, loc
emitter.echo(SUCCESSFUL_DISABLE_RESTAKING.format(staking_address=staking_address), color='green', verbosity=1)
paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
return # Exit
@stake.command()
@ -569,12 +583,10 @@ def restake(general_config, transacting_staker_options, config_file, enable, loc
@option_force
@group_general_config
def winddown(general_config, transacting_staker_options, config_file, enable, lock_until, force):
"""
Manage winding down with --enable or --disable.
"""
emitter = _setup_emitter(general_config)
"""Manage winding down with --enable or --disable."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
@ -587,7 +599,6 @@ def winddown(general_config, transacting_staker_options, config_file, enable, lo
# Authenticate
password = transacting_staker_options.get_password(blockchain, client_account)
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
# Inner Exclusive Switch
@ -603,6 +614,7 @@ def winddown(general_config, transacting_staker_options, config_file, enable, lo
emitter.echo(SUCCESSFUL_DISABLE_WIND_DOWN.format(staking_address=staking_address), color='green', verbosity=1)
paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
return # Exit
@stake.command()
@ -614,12 +626,10 @@ def winddown(general_config, transacting_staker_options, config_file, enable, lo
@click.option('--index', help="A specific stake index to resume", type=click.INT)
@group_general_config
def divide(general_config, transacting_staker_options, config_file, force, value, lock_periods, index):
"""
Create a new stake from part of an existing one.
"""
"""Create a new stake from part of an existing one."""
# Setup
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
economics = STAKEHOLDER.economics
@ -698,6 +708,7 @@ def divide(general_config, transacting_staker_options, config_file, force, value
# Show the resulting stake list
paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
return # Exit
@stake.command()
@ -711,7 +722,7 @@ def prolong(general_config, transacting_staker_options, config_file, force, lock
"""Prolong an existing stake's duration."""
# Setup
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
action_period = STAKEHOLDER.staking_agent.get_current_period()
blockchain = transacting_staker_options.get_blockchain()
@ -777,15 +788,17 @@ def prolong(general_config, transacting_staker_options, config_file, force, lock
@click.option('--withdraw-address', help="Send fee collection to an alternate address", type=EIP55_CHECKSUM_ADDRESS)
@option_force
@group_general_config
def collect_reward(general_config, transacting_staker_options, config_file,
staking_reward, policy_fee, withdraw_address, force):
"""
Withdraw staking reward.
"""
### Setup ###
emitter = _setup_emitter(general_config)
def collect_reward(general_config,
transacting_staker_options,
config_file,
staking_reward,
policy_fee,
withdraw_address,
force):
"""Withdraw staking reward."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
@ -818,7 +831,7 @@ def collect_reward(general_config, transacting_staker_options, config_file,
paint_receipt_summary(receipt=policy_receipt,
chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
emitter=emitter)
return # Exit
@stake.command()
@click.argument('action', type=click.Choice(['status', 'withdraw']))
@ -827,26 +840,20 @@ def collect_reward(general_config, transacting_staker_options, config_file,
@option_force
@group_general_config
def preallocation(general_config, transacting_staker_options, config_file, action, force):
"""
Claim token rewards collected by a preallocation contract.
"""
### Setup ###
emitter = _setup_emitter(general_config)
"""Claim token rewards collected by a preallocation contract."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
# Unauthenticated actions: status
if action == 'status':
paint_preallocation_status(emitter=emitter,
token_agent=STAKEHOLDER.token_agent,
preallocation_agent=STAKEHOLDER.preallocation_escrow_agent)
return
return paint_preallocation_status(emitter=emitter,
token_agent=STAKEHOLDER.token_agent,
preallocation_agent=STAKEHOLDER.preallocation_escrow_agent)
# Authenticated actions: withdraw-tokens
client_account, staking_address = handle_client_account_for_staking(
emitter=emitter,
stakeholder=STAKEHOLDER,
@ -854,9 +861,10 @@ def preallocation(general_config, transacting_staker_options, config_file, actio
individual_allocation=STAKEHOLDER.individual_allocation,
force=force)
# Authenticate
password = transacting_staker_options.get_password(blockchain, client_account)
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
if action == 'withdraw':
token_balance = NU.from_nunits(STAKEHOLDER.token_agent.get_balance(staking_address))
locked_tokens = NU.from_nunits(STAKEHOLDER.preallocation_escrow_agent.unvested_tokens)
@ -868,6 +876,7 @@ def preallocation(general_config, transacting_staker_options, config_file, actio
paint_receipt_summary(receipt=receipt,
chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
emitter=emitter)
return # Exit
@stake.command()
@ -876,13 +885,10 @@ def preallocation(general_config, transacting_staker_options, config_file, actio
@option_event_name
@group_general_config
def events(general_config, staker_options, config_file, event_name):
"""
See blockchain events associated to a staker
"""
### Setup ###
emitter = _setup_emitter(general_config)
"""See blockchain events associated to a staker"""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = staker_options.create_character(emitter, config_file)
blockchain = staker_options.get_blockchain()
@ -910,6 +916,7 @@ def events(general_config, staker_options, config_file, event_name):
entries = event_filter.get_all_entries()
for event_record in entries:
emitter.echo(f" - {EventRecord(event_record)}")
return # Exit
@stake.command('set-min-rate')
@ -919,11 +926,10 @@ def events(general_config, staker_options, config_file, event_name):
@group_general_config
@click.option('--min-rate', help="Minimum acceptable fee rate, set by staker", type=WEI)
def set_min_rate(general_config, transacting_staker_options, config_file, force, min_rate):
"""
Staker sets the minimum acceptable fee rate for their associated worker.
"""
emitter = _setup_emitter(general_config)
"""Staker sets the minimum acceptable fee rate for their associated worker."""
# Setup
emitter = setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
blockchain = transacting_staker_options.get_blockchain()
@ -939,17 +945,15 @@ def set_min_rate(general_config, transacting_staker_options, config_file, force,
# TODO check range
min_rate = click.prompt(PROMPT_STAKER_MIN_POLICY_RATE, type=WEI)
password = transacting_staker_options.get_password(blockchain, client_account)
if not force:
click.confirm(CONFIRM_NEW_MIN_POLICY_RATE.format(min_rate=min_rate), abort=True)
STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
receipt = STAKEHOLDER.set_min_fee_rate(min_rate=min_rate)
# Report Success
message = SUCCESSFUL_SET_MIN_POLICY_RATE.format(min_rate=min_rate, staking_address=staking_address)
emitter.echo(message, color='green')
paint_receipt_summary(emitter=emitter,
receipt=receipt,
chain_name=blockchain.client.chain_name,
transaction_type='set_min_rate')
return paint_receipt_summary(emitter=emitter,
receipt=receipt,
chain_name=blockchain.client.chain_name,
transaction_type='set_min_rate')

View File

@ -27,8 +27,7 @@ from nucypher.blockchain.eth.constants import (
STAKING_ESCROW_CONTRACT_NAME
)
from nucypher.blockchain.eth.utils import datetime_at_period
from nucypher.characters.banners import NU_BANNER
from nucypher.cli.actions.utils import connect_to_blockchain, get_registry
from nucypher.cli.actions.utils import connect_to_blockchain, get_registry, setup_emitter
from nucypher.cli.config import group_general_config
from nucypher.cli.options import (
group_options,
@ -46,7 +45,7 @@ from nucypher.cli.painting.policies import paint_fee_rate_range
from nucypher.cli.painting.staking import paint_stakers
from nucypher.cli.painting.status import paint_contract_status, paint_locked_tokens_status
from nucypher.config.constants import NUCYPHER_ENVVAR_PROVIDER_URI
from nucypher.cli.actions import
class RegistryOptions:
@ -61,7 +60,7 @@ class RegistryOptions:
self.network = network
def setup(self, general_config) -> tuple:
emitter = _setup_emitter(general_config)
emitter = setup_emitter(general_config)
registry = get_registry(network=self.network, registry_filepath=self.registry_filepath)
blockchain = connect_to_blockchain(emitter=emitter, provider_uri=self.provider_uri)
return emitter, registry, blockchain
@ -78,12 +77,6 @@ group_registry_options = group_options(
)
def _setup_emitter(general_config):
emitter = general_config.emitter
emitter.banner(NU_BANNER)
return emitter
@click.group()
def status():
"""Echo a snapshot of live NuCypher Network metadata."""

View File

@ -15,27 +15,52 @@ 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_BLOCKCHAIN_CONNECTION
from nucypher.blockchain.economics import EconomicsFactory
from nucypher.blockchain.eth.utils import datetime_at_period
from nucypher.characters.banners import URSULA_BANNER
from nucypher.cli.actions.auth import get_client_password, get_nucypher_password
from nucypher.cli.actions.config import (destroy_configuration, get_or_update_configuration, get_provider_process,
handle_missing_configuration_file)
from nucypher.cli.actions.config import (
destroy_configuration,
get_or_update_configuration,
get_provider_process,
handle_missing_configuration_file
)
from nucypher.cli.actions.network import determine_external_ip_address
from nucypher.cli.actions.select import select_client_account, select_config_file, select_network
from nucypher.cli.actions.utils import make_cli_character
from nucypher.cli.actions.utils import make_cli_character, setup_emitter
from nucypher.cli.commands.deploy import option_gas_strategy
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import CONFIRMING_ACTIVITY_NOW, DEVELOPMENT_MODE_WARNING, FORCE_MODE_WARNING, \
SUCCESSFUL_CONFIRM_ACTIVITY, SUCCESSFUL_MANUALLY_SAVE_METADATA
from nucypher.cli.options import (group_options, option_config_file, option_config_root, option_db_filepath, option_dev,
option_dry_run, option_federated_only, option_force, option_geth, option_light,
option_min_stake, option_network, option_poa, option_provider_uri,
option_registry_filepath, option_signer_uri, option_teacher_uri)
from nucypher.cli.literature import (
CONFIRMING_ACTIVITY_NOW,
DEVELOPMENT_MODE_WARNING,
FORCE_MODE_WARNING,
SUCCESSFUL_CONFIRM_ACTIVITY,
SUCCESSFUL_MANUALLY_SAVE_METADATA
)
from nucypher.cli.options import (
group_options,
option_config_file,
option_config_root,
option_db_filepath,
option_dev,
option_dry_run,
option_federated_only,
option_force,
option_geth,
option_light,
option_min_stake,
option_network,
option_poa,
option_provider_uri,
option_registry_filepath,
option_signer_uri,
option_teacher_uri
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.painting.transactions import paint_receipt_summary
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, NETWORK_PORT
@ -49,9 +74,22 @@ class UrsulaConfigOptions:
__option_name__ = 'config_options'
def __init__(self, geth, provider_uri, worker_address, federated_only, rest_host,
rest_port, db_filepath, network, registry_filepath, dev, poa, light,
gas_strategy, signer_uri, availability_check):
def __init__(self,
geth,
provider_uri,
worker_address,
federated_only,
rest_host,
rest_port,
db_filepath,
network,
registry_filepath,
dev,
poa,
light,
gas_strategy,
signer_uri,
availability_check):
if federated_only:
if geth:
@ -247,14 +285,13 @@ group_character_options = group_options(
config_options=group_config_options,
lonely=click.option('--lonely', help="Do not connect to seednodes", is_flag=True),
teacher_uri=option_teacher_uri,
min_stake=option_min_stake)
min_stake=option_min_stake
)
@click.group()
def ursula():
"""
"Ursula the Untrusted" PRE Re-encryption node management commands.
"""
""""Ursula the Untrusted" PRE Re-encryption node management commands."""
@ursula.command()
@ -266,14 +303,14 @@ def init(general_config, config_options, force, config_root):
"""
Create a new Ursula node configuration.
"""
emitter = _setup_emitter(general_config, config_options.worker_address)
emitter = setup_emitter(general_config, config_options.worker_address)
_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.domains: # TODO: Again, weird network/domains mapping. See UrsulaConfigOptions' constructor. #1580
config_options.domains = {select_network(emitter)}
ursula_config = config_options.generate_config(emitter, config_root, force)
paint_new_installation_help(emitter, new_configuration=ursula_config)
return paint_new_installation_help(emitter, new_configuration=ursula_config)
@ursula.command()
@ -285,14 +322,14 @@ def destroy(general_config, config_options, config_file, force):
"""
Delete Ursula node configuration.
"""
emitter = _setup_emitter(general_config, config_options.worker_address)
emitter = setup_emitter(general_config, config_options.worker_address)
_pre_launch_warnings(emitter, dev=config_options.dev, force=force)
if not config_file:
config_file = select_config_file(emitter=emitter,
checksum_address=config_options.worker_address,
config_class=UrsulaConfiguration)
ursula_config = config_options.create_config(emitter, config_file)
destroy_configuration(emitter, character_config=ursula_config, force=force)
return destroy_configuration(emitter, character_config=ursula_config, force=force)
@ursula.command()
@ -300,13 +337,11 @@ def destroy(general_config, config_options, config_file, force):
@option_config_file
@group_general_config
def forget(general_config, config_options, config_file):
"""
Forget all known nodes.
"""
emitter = _setup_emitter(general_config, config_options.worker_address)
"""Forget all known nodes."""
emitter = setup_emitter(general_config, config_options.worker_address)
_pre_launch_warnings(emitter, dev=config_options.dev, force=None)
ursula_config = config_options.create_config(emitter, config_file)
forget(emitter, configuration=ursula_config)
return forget(emitter, configuration=ursula_config)
@ursula.command()
@ -321,7 +356,7 @@ def run(general_config, character_options, config_file, interactive, dry_run, me
"""Run an "Ursula" node."""
worker_address = character_options.config_options.worker_address
emitter = _setup_emitter(general_config, worker_address=worker_address)
emitter = setup_emitter(general_config)
_pre_launch_warnings(emitter, dev=character_options.config_options.dev, force=None)
if not character_options.config_options.dev and not config_file:
@ -329,11 +364,9 @@ def run(general_config, character_options, config_file, interactive, dry_run, me
checksum_address=worker_address,
config_class=UrsulaConfiguration)
ursula_config, URSULA = character_options.create_character(
emitter=emitter,
config_file=config_file,
json_ipc=general_config.json_ipc
)
ursula_config, URSULA = character_options.create_character(emitter=emitter,
config_file=config_file,
json_ipc=general_config.json_ipc)
return URSULA.run(emitter=emitter,
start_reactor=not dry_run,
@ -346,10 +379,8 @@ def run(general_config, character_options, config_file, interactive, dry_run, me
@option_config_file
@group_general_config
def save_metadata(general_config, character_options, config_file):
"""
Manually write node metadata to disk without running.
"""
emitter = _setup_emitter(general_config, character_options.config_options.worker_address)
"""Manually write node metadata to disk without running."""
emitter = setup_emitter(general_config, character_options.config_options.worker_address)
_pre_launch_warnings(emitter, dev=character_options.config_options.dev, force=None)
_, URSULA = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
metadata_path = URSULA.write_node_metadata(node=URSULA)
@ -361,10 +392,8 @@ def save_metadata(general_config, character_options, config_file):
@option_config_file
@group_general_config
def config(general_config, config_options, config_file):
"""
View and optionally update the Ursula node's configuration.
"""
emitter = _setup_emitter(general_config, config_options.worker_address)
"""View and optionally update the Ursula node's configuration."""
emitter = setup_emitter(general_config, config_options.worker_address)
if not config_file:
config_file = select_config_file(emitter=emitter,
checksum_address=config_options.worker_address,
@ -385,7 +414,7 @@ def commit_to_next_period(general_config, character_options, config_file):
"""Manually make a commitment to the next period."""
# Setup
emitter = _setup_emitter(general_config, character_options.config_options.worker_address)
emitter = setup_emitter(general_config, character_options.config_options.worker_address)
_pre_launch_warnings(emitter, dev=character_options.config_options.dev, force=None)
_, URSULA = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
@ -406,15 +435,6 @@ def commit_to_next_period(general_config, character_options, config_file):
# TODO: Check CommitmentMade event (see #1193)
def _setup_emitter(general_config, worker_address):
# Banner
emitter = general_config.emitter
emitter.clear()
emitter.banner(URSULA_BANNER.format(worker_address or ''))
return emitter
def _pre_launch_warnings(emitter, dev, force):
if dev:
emitter.echo(DEVELOPMENT_MODE_WARNING, color='yellow', verbosity=1)

View File

@ -29,22 +29,46 @@ from nucypher.blockchain.eth.agents import ContractAgency, WorkLockAgent
from nucypher.blockchain.eth.signers import Signer
from nucypher.blockchain.eth.token import NU
from nucypher.blockchain.eth.utils import prettify_eth_amount
from nucypher.characters.banners import WORKLOCK_BANNER
from nucypher.cli.actions.auth import get_client_password
from nucypher.cli.actions.select import select_client_account
from nucypher.cli.actions.utils import connect_to_blockchain, get_registry
from nucypher.cli.actions.utils import connect_to_blockchain, get_registry, setup_emitter
from nucypher.cli.config import group_general_config
from nucypher.cli.literature import AVAILABLE_CLAIM_NOTICE, BIDDERS_ALREADY_VERIFIED, BIDDING_WINDOW_CLOSED, \
BIDS_VALID_NO_FORCE_REFUND_INDICATED, CLAIM_ALREADY_PLACED, COMPLETED_BID_VERIFICATION, CONFIRM_BID_VERIFICATION, \
CONFIRM_COLLECT_WORKLOCK_REFUND, CONFIRM_REQUEST_WORKLOCK_COMPENSATION, CONFIRM_WORKLOCK_CLAIM, \
PROMPT_BID_VERIFY_GAS_LIMIT, REQUESTING_WORKLOCK_COMPENSATION, SUBMITTING_WORKLOCK_CLAIM, \
SUBMITTING_WORKLOCK_REFUND_REQUEST, SUCCESSFUL_BID_CANCELLATION, WHALE_WARNING, \
WORKLOCK_ADDITIONAL_COMPENSATION_AVAILABLE, WORKLOCK_CLAIM_ADVISORY
from nucypher.cli.options import (group_options, option_force, option_hw_wallet, option_network, option_provider_uri,
option_registry_filepath, option_signer_uri)
from nucypher.cli.literature import (
AVAILABLE_CLAIM_NOTICE,
BIDDERS_ALREADY_VERIFIED,
BIDDING_WINDOW_CLOSED,
BIDS_VALID_NO_FORCE_REFUND_INDICATED,
CLAIM_ALREADY_PLACED,
COMPLETED_BID_VERIFICATION,
CONFIRM_BID_VERIFICATION,
CONFIRM_COLLECT_WORKLOCK_REFUND,
CONFIRM_REQUEST_WORKLOCK_COMPENSATION,
CONFIRM_WORKLOCK_CLAIM,
PROMPT_BID_VERIFY_GAS_LIMIT,
REQUESTING_WORKLOCK_COMPENSATION,
SUBMITTING_WORKLOCK_CLAIM,
SUBMITTING_WORKLOCK_REFUND_REQUEST,
SUCCESSFUL_BID_CANCELLATION,
WHALE_WARNING,
WORKLOCK_ADDITIONAL_COMPENSATION_AVAILABLE,
WORKLOCK_CLAIM_ADVISORY
)
from nucypher.cli.options import (
group_options,
option_force,
option_hw_wallet,
option_network,
option_provider_uri,
option_registry_filepath,
option_signer_uri
)
from nucypher.cli.painting.transactions import paint_receipt_summary
from nucypher.cli.painting.worklock import paint_bidder_status, paint_bidding_notice, paint_worklock_claim, \
from nucypher.cli.painting.worklock import (
paint_bidder_status,
paint_bidding_notice,
paint_worklock_claim,
paint_worklock_status
)
from nucypher.cli.types import DecimalRange, EIP55_CHECKSUM_ADDRESS
from nucypher.config.constants import NUCYPHER_ENVVAR_PROVIDER_URI
@ -53,12 +77,6 @@ option_bidder_address = click.option('--bidder-address',
type=EIP55_CHECKSUM_ADDRESS)
def _setup_emitter(general_config, network: str):
emitter = general_config.emitter
emitter.banner(WORKLOCK_BANNER.format(network))
return emitter
class WorkLockOptions:
__option_name__ = 'worklock_options'
@ -77,7 +95,7 @@ class WorkLockOptions:
self.network = network
def setup(self, general_config) -> tuple:
emitter = _setup_emitter(general_config, network=self.network.capitalize())
emitter = setup_emitter(general_config) # TODO: Restore Banner: network=self.network.capitalize()
registry = get_registry(network=self.network, registry_filepath=self.registry_filepath)
blockchain = connect_to_blockchain(emitter=emitter, provider_uri=self.provider_uri)
return emitter, registry, blockchain
@ -118,10 +136,7 @@ group_worklock_options = group_options(
@click.group()
def worklock():
"""
Participate in NuCypher's WorkLock to obtain NU tokens
"""
pass
"""Participate in NuCypher's WorkLock to obtain NU tokens"""
@worklock.command()

View File

@ -28,7 +28,6 @@ from nucypher.utilities.logging import GlobalLoggerSettings
class GroupGeneralConfig:
__option_name__ = 'general_config'
verbosity = 0
@ -40,15 +39,15 @@ class GroupGeneralConfig:
log_to_file = get_env_bool("NUCYPHER_FILE_LOGS", True)
def __init__(self,
json_ipc,
verbose,
quiet,
no_logs,
console_logs,
file_logs,
sentry_logs,
log_level,
debug):
json_ipc: bool,
verbose: bool,
quiet: bool,
no_logs: bool,
console_logs: bool,
file_logs: bool,
sentry_logs: bool,
log_level: bool,
debug: bool):
self.log = Logger(self.__class__.__name__)
@ -78,11 +77,9 @@ class GroupGeneralConfig:
self.emitter.message("Verbose mode is enabled", color='blue')
# Logging
if debug and no_logs:
raise click.BadOptionUsage(
option_name="no-logs",
message="--debug and --no-logs cannot be used at the same time.")
message = "--debug and --no-logs cannot be used at the same time."
raise click.BadOptionUsage(option_name="no-logs", message=message)
# Defaults
if file_logs is None:
@ -121,28 +118,39 @@ class GroupGeneralConfig:
group_general_config = group_options(
GroupGeneralConfig,
json_ipc=click.option('-J', '--json-ipc', help="Send all IPC output to stdout as JSON, and turn off the rest", is_flag=True),
verbose=click.option('-v', '--verbose', help="Verbose console messages", is_flag=True),
quiet=click.option('-Q', '--quiet', help="Disable console messages", is_flag=True),
no_logs=click.option('-L', '--no-logs', help="Disable all logging output", is_flag=True),
console_logs=click.option('--console-logs/--no-console-logs',
help="Enable/disable logging to console. "
"Defaults to `--no-console-logs`.",
default=False),
file_logs=click.option('--file-logs/--no-file-logs',
help="Enable/disable logging to file. "
"Defaults to NUCYPHER_FILE_LOGS, or to `--file-logs` if it is not set.",
default=None),
sentry_logs=click.option('--sentry-logs/--no-sentry-logs',
help="Enable/disable logging to Sentry. "
"Defaults to NUCYPHER_SENTRY_LOGS, or to `--sentry-logs` if it is not set.",
default=None),
log_level=click.option('--log-level', help="The log level for this process. Is overridden by --debug.",
type=click.Choice(['critical', 'error', 'warn', 'info', 'debug']),
default='info'),
debug=click.option('-D', '--debug',
help="Enable debugging mode, crashing on more exceptions instead of trying to recover. "
"Also sets log level to \"debug\", turns on console and file logging "
"and turns off Sentry logging.",
is_flag=True),
)
json_ipc=click.option('-J', '--json-ipc',
help="Send all IPC output to stdout as JSON, and turn off the rest",
is_flag=True),
console_logs=click.option(
'--console-logs/--no-console-logs',
help="Enable/disable logging to console. Defaults to `--no-console-logs`.",
default=False),
file_logs=click.option(
'--file-logs/--no-file-logs',
help="Enable/disable logging to file. Defaults to NUCYPHER_FILE_LOGS, or to `--file-logs` if it is not set.",
default=None),
sentry_logs=click.option(
'--sentry-logs/--no-sentry-logs',
help="Enable/disable logging to Sentry. Defaults to NUCYPHER_SENTRY_LOGS, or to `--sentry-logs` if it is not set.",
default=None),
log_level=click.option(
'--log-level', help="The log level for this process. Is overridden by --debug.",
type=click.Choice(['critical', 'error', 'warn', 'info', 'debug']),
default='info'),
debug=click.option(
'-D', '--debug',
help="Enable debugging mode, crashing on more exceptions instead of trying to recover. "
"Also sets log level to \"debug\", turns on console and file logging "
"and turns off Sentry logging.",
is_flag=True),
)

View File

@ -130,7 +130,7 @@ SUCCESSFUL_DISABLE_WIND_DOWN = 'Successfully disabled winding down for {staking_
#
# Restaking Lock
# Restaking
#
RESTAKING_LOCK_AGREEMENT = """
@ -142,11 +142,6 @@ CONFIRM_RESTAKING_LOCK = "Confirm enable re-staking lock for staker {staking_add
SUCCESSFUL_ENABLE_RESTAKE_LOCK = 'Successfully enabled re-staking lock for {staking_address} until {lock_until}'
#
# Restaking
#
RESTAKING_AGREEMENT = """
By enabling the re-staking for {staking_address}, all staking rewards will be automatically added to your existing stake.
"""

View File

@ -24,7 +24,7 @@ from nucypher.cli.painting.help import echo_version
@click.group()
@click.option('--version', help="Echo the CLI version", is_flag=True, callback=echo_version, expose_value=False, is_eager=True)
def nucypher_cli():
pass
"""Top level command for all things nucypher."""
#
@ -54,17 +54,17 @@ def nucypher_cli():
ENTRY_POINTS = (
# Characters
alice.alice, # Author of Policies
bob.bob, # Builder of Capsules
enrico.enrico, # Encryptor of Data
ursula.ursula, # Untrusted Re-Encryption Proxy
alice.alice, # Author of Policies
bob.bob, # Builder of Capsules
enrico.enrico, # Encryptor of Data
ursula.ursula, # Untrusted Re-Encryption Proxy
# Utility Commands
stake.stake, # Stake Management
status.status, # Network Status
felix.felix, # Faucet
stake.stake, # Stake Management
status.status, # Network Status
felix.felix, # Faucet
multisig.multisig, # MultiSig operations
worklock.worklock # WorkLock
worklock.worklock # WorkLock
)
for entry_point in ENTRY_POINTS:

View File

@ -27,6 +27,13 @@ def echo_version(ctx, param, value):
ctx.exit()
def echo_solidity_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.secho(f"Supported solidity version: {SOLIDITY_COMPILER_VERSION}", bold=True)
ctx.exit()
def paint_new_installation_help(emitter, new_configuration):
character_config_class = new_configuration.__class__
character_name = character_config_class.NAME.lower()
@ -54,10 +61,3 @@ def paint_new_installation_help(emitter, new_configuration):
how_to_run_message = f"\nTo run {adjective} {character_name.capitalize()} node from the default configuration filepath run: \n\n'{suggested_command}'\n"
emitter.echo(how_to_run_message.format(suggested_command), color='green')
def echo_solidity_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.secho(f"Supported solidity version: {SOLIDITY_COMPILER_VERSION}", bold=True)
ctx.exit()

View File

@ -17,8 +17,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from collections import Counter
from typing import List
import maya
from typing import List
from web3.main import Web3
from nucypher.blockchain.eth.agents import AdjudicatorAgent, ContractAgency, NucypherTokenAgent, PolicyManagerAgent, \
@ -27,6 +28,7 @@ from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.token import NU
from nucypher.blockchain.eth.utils import prettify_eth_amount
from nucypher.network.nicknames import nickname_from_seed
def paint_contract_status(registry, emitter):

View File

@ -14,6 +14,8 @@ 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/>.
"""
from collections import deque
import maya

View File

@ -187,7 +187,7 @@ def test_cancel_bid(click_runner,
mock_worklock_agent.assert_contract_calls(calls=expected_calls)
@pytest.mark.skip
@pytest.mark.skip # TODO
def test_post_initialization(click_runner,
mocker,
mock_worklock_agent,