Exemple #1
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               stakers_addresses: Iterable[str],
                               workers_addresses: Iterable[str],
                               commit_to_next_period: bool = False,
                               **ursula_overrides) -> List[Ursula]:

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    stakers_and_workers = zip(stakers_addresses, workers_addresses)
    ursulas = list()

    for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port):
        ursula = ursula_config.produce(checksum_address=staker_address,
                                       worker_address=worker_address,
                                       db_filepath=MOCK_URSULA_DB_FILEPATH,
                                       rest_port=port + 100,
                                       **ursula_overrides)
        if commit_to_next_period:
            ursula.transacting_power.activate()
            ursula.commit_to_next_period()

        ursulas.append(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_interface.port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    return ursulas
Exemple #2
0
def test_ursula_development_configuration(federated_only=True):
    config = UrsulaConfiguration(dev_mode=True, federated_only=federated_only)
    assert config.is_me is True
    assert config.dev_mode is True
    assert config.keyring == NO_KEYRING_ATTACHED

    # Produce an Ursula
    ursula_one = config()

    # Ensure we do in fact have an Ursula here
    assert isinstance(ursula_one, Ursula)
    assert len(ursula_one.checksum_address) == 42
    assert ursula_one.federated_only is federated_only

    # A Temporary Ursula
    port = ursula_one.rest_information()[0].port
    assert port == UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT
    assert tempfile.gettempdir() in ursula_one.datastore.engine.url.database
    assert ursula_one.certificate_filepath is CERTIFICATE_NOT_SAVED
    assert UrsulaConfiguration.TEMP_CONFIGURATION_DIR_PREFIX in ursula_one.keyring_root
    assert isinstance(ursula_one.node_storage, ForgetfulNodeStorage)
    assert ursula_one.node_storage._name == ":memory:"

    # Alternate way to produce a character with a direct call
    ursula_two = config.produce()
    assert isinstance(ursula_two, Ursula)

    # All development Ursulas are unique
    ursulas = [ursula_one, ursula_two]
    for _ in range(3):
        ursula = config()
        assert ursula not in ursulas
        ursulas.append(ursula)
Exemple #3
0
def make_federated_ursulas(ursula_config: UrsulaConfiguration,
                           quantity: int = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK,
                           know_each_other: bool = True,
                           **ursula_overrides) -> Set[Ursula]:

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    federated_ursulas = set()
    for port in range(starting_port, starting_port+quantity):

        ursula = ursula_config.produce(rest_port=port + 100,
                                       db_filepath=MOCK_URSULA_DB_FILEPATH,
                                       **ursula_overrides)

        federated_ursulas.add(ursula)

        # Store this Ursula in our global testing cache.

        port = ursula.rest_interface.port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    if know_each_other:
        for ursula_to_teach in federated_ursulas:
            # Add other Ursulas as known nodes.
            for ursula_to_learn_about in federated_ursulas:
                ursula_to_teach.remember_node(ursula_to_learn_about)

    return federated_ursulas
Exemple #4
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               blockchain: BlockchainInterface,
                               stakers_addresses: Iterable[str],
                               workers_addresses: Iterable[str],
                               confirm_activity: bool = False,
                               **ursula_overrides) -> List[Ursula]:

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    stakers_and_workers = zip(stakers_addresses, workers_addresses)
    ursulas = list()

    stake_tracker = StakeTracker(checksum_addresses=list(stakers_addresses))
    for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port):

        ursula = ursula_config.produce(checksum_address=staker_address,
                                       worker_address=worker_address,
                                       blockchain=blockchain,
                                       db_filepath=MOCK_URSULA_DB_FILEPATH,
                                       rest_port=port + 100,
                                       stake_tracker=stake_tracker,
                                       **ursula_overrides)
        if confirm_activity:
            ursula.confirm_activity()

        ursulas.append(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_interface.port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    return ursulas
Exemple #5
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               stakers_addresses: Iterable[str],
                               workers_addresses: Iterable[str],
                               commit_to_next_period: bool = False,
                               **ursula_overrides) -> List[Ursula]:

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    stakers_and_workers = zip(stakers_addresses, workers_addresses)
    ursulas = list()

    for port, (staker_address, worker_address) in enumerate(stakers_and_workers, start=starting_port):
        ursula = ursula_config.produce(checksum_address=staker_address,
                                       worker_address=worker_address,
                                       db_filepath=tempfile.mkdtemp(),
                                       rest_port=port + 100,
                                       **ursula_overrides)
        if commit_to_next_period:
            # TODO: Is _crypto_power trying to be public?  Or is there a way to expose *something* public about TransactingPower?
            # Do we need to revisit the concept of "public material"?  Or does this rightly belong as a method?
            tx_power = ursula._crypto_power.power_ups(TransactingPower)
            tx_power.activate()
            ursula.commit_to_next_period()

        ursulas.append(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_interface.port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    return ursulas
Exemple #6
0
def run_ursula(rest_port,
               rest_host,
               db_name,
               checksum_address,
               federated_only,
               metadata_dir,
               config_file,
               dev
               ) -> None:
    """

    The following procedure is required to "spin-up" an Ursula node.

        1. Initialize UrsulaConfiguration
        2. Initialize Ursula
        3. Run TLS deployment
        4. Start the staking daemon

    Configurable values are first read from the configuration file,
    but can be overridden (mostly for testing purposes) with inline cli options.

    """
    if not dev:
        click.echo("WARNING: Development mode is disabled")
        temp = False
    else:
        click.echo("Running in development mode")
        temp = True

    if config_file:
        ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file)
    else:
        ursula_config = UrsulaConfiguration(temp=temp,
                                            auto_initialize=temp,
                                            rest_host=rest_host,
                                            rest_port=rest_port,
                                            db_name=db_name,
                                            is_me=True,
                                            federated_only=federated_only,
                                            checksum_address=checksum_address,
                                            # save_metadata=False,  # TODO
                                            load_metadata=True,
                                            known_metadata_dir=metadata_dir,
                                            start_learning_now=True,
                                            abort_on_learning_error=temp)
    try:
        URSULA = ursula_config.produce()
        URSULA.get_deployer().run()       # Run TLS Deploy (Reactor)
        if not URSULA.federated_only:     # TODO: Resume / Init
            URSULA.stake()                # Start Staking Daemon
    finally:
        click.echo("Cleaning up temporary runtime files and directories")
        ursula_config.cleanup()  # TODO: Integrate with other "graceful" shutdown functionality
        click.echo("Exited gracefully")
Exemple #7
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               ether_addresses: Union[list, int],
                               stake: bool = False,
                               know_each_other: bool = True,
                               **ursula_overrides) -> Set[Ursula]:

    # Alternately accepts an int of the quantity of ursulas to make
    if isinstance(ether_addresses, int):
        ether_addresses = [
            to_checksum_address(secure_random(20))
            for _ in range(ether_addresses)
        ]

    if not TEST_KNOWN_URSULAS_CACHE:
        starting_port = TEST_URSULA_STARTING_PORT
    else:
        starting_port = max(TEST_KNOWN_URSULAS_CACHE.keys()) + 1

    ursulas = set()
    for port, checksum_address in enumerate(ether_addresses,
                                            start=starting_port):

        ursula = ursula_config.produce(checksum_address=checksum_address,
                                       db_name="test-{}".format(port),
                                       rest_port=port + 100,
                                       **ursula_overrides)
        if stake is True:

            min_stake, balance = int(
                constants.MIN_ALLOWED_LOCKED), ursula.token_balance
            amount = random.randint(min_stake, balance)

            # for a random lock duration
            min_locktime, max_locktime = int(
                constants.MIN_LOCKED_PERIODS), int(
                    constants.MAX_MINTING_PERIODS)
            periods = random.randint(min_locktime, max_locktime)

            ursula.initialize_stake(amount=amount, lock_periods=periods)

        ursulas.add(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_information()[0].port
        TEST_KNOWN_URSULAS_CACHE[port] = ursula

    if know_each_other:

        for ursula_to_teach in ursulas:
            # Add other Ursulas as known nodes.
            for ursula_to_learn_about in ursulas:
                ursula_to_teach.remember_node(ursula_to_learn_about)

    return ursulas
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               ether_addresses: Union[list, int],
                               stake: bool = False,
                               know_each_other: bool = True,
                               economics: TokenEconomics = None,
                               **ursula_overrides) -> List[Ursula]:

    if not economics:
        economics = TokenEconomics()

    # Alternately accepts an int of the quantity of ursulas to make
    if isinstance(ether_addresses, int):
        ether_addresses = [to_checksum_address(secure_random(20)) for _ in range(ether_addresses)]

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    ursulas = list()
    for port, checksum_address in enumerate(ether_addresses, start=starting_port):

        ursula = ursula_config.produce(checksum_public_address=checksum_address,
                                       db_filepath=MOCK_URSULA_DB_FILEPATH,
                                       rest_port=port + 100,
                                       **ursula_overrides)
        if stake is True:

            min_stake, balance = economics.minimum_allowed_locked, ursula.token_balance
            amount = random.randint(min_stake, balance)

            # for a random lock duration
            min_locktime, max_locktime = economics.minimum_locked_periods, economics.maximum_locked_periods
            periods = random.randint(min_locktime, max_locktime)

            ursula.initialize_stake(amount=amount, lock_periods=periods)

        ursulas.append(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_information()[0].port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    if know_each_other:

        for ursula_to_teach in ursulas:
            # Add other Ursulas as known nodes.
            for ursula_to_learn_about in ursulas:
                ursula_to_teach.remember_node(ursula_to_learn_about)

    return ursulas
def test_federated_ursula_development_configuration():

    # Configure & Produce Ursula
    ursula_config = UrsulaConfiguration(dev_mode=True, federated_only=True)
    ursula = ursula_config.produce()

    # Network Port
    port = ursula.rest_information()[0].port
    assert port == UrsulaConfiguration.DEFAULT_DEVELOPMENT_REST_PORT

    # Database
    assert tempfile.gettempdir() in ursula.datastore.engine.url.database

    # TLS Certificate
    assert ursula.certificate_filepath is CERTIFICATE_NOT_SAVED
Exemple #10
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               staking_provider_addresses: Iterable[str],
                               operator_addresses: Iterable[str],
                               commit_now=True,
                               **ursula_overrides) -> List[Ursula]:

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    providers_and_operators = zip(staking_provider_addresses,
                                  operator_addresses)
    ursulas = list()

    for port, (staking_provider_address,
               operator_address) in enumerate(providers_and_operators,
                                              start=starting_port):
        ursula = ursula_config.produce(
            checksum_address=staking_provider_address,
            operator_address=operator_address,
            db_filepath=MOCK_DB,
            rest_port=port + 100,
            **ursula_overrides)

        # TODO: Confirm operator here?
        # if commit_now:
        #     ursula.confirm_operator_address()

        ursulas.append(ursula)

        # Store this Ursula in our global testing cache.
        port = ursula.rest_interface.port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    return ursulas
Exemple #11
0
def ursula(
        click_config,
        action,
        debug,
        dev,
        quiet,
        dry_run,
        force,
        lonely,
        network,
        teacher_uri,
        min_stake,
        rest_host,
        rest_port,
        db_filepath,
        checksum_address,
        federated_only,
        poa,
        config_root,
        config_file,
        metadata_dir,  # TODO: Start nodes from an additional existing metadata dir
        provider_uri,
        no_registry,
        registry_filepath) -> None:
    """
    Manage and run an "Ursula" PRE node.

    \b
    Actions
    -------------------------------------------------
    \b
    run            Run an "Ursula" node.
    init           Create a new Ursula node configuration.
    view           View the Ursula node's configuration.
    forget         Forget all known nodes.
    save-metadata  Manually write node metadata to disk without running
    destroy        Delete Ursula node configuration.

    """

    #
    # Boring Setup Stuff
    #
    if not quiet:
        click.secho(URSULA_BANNER)
        log = Logger('ursula.cli')

    if debug and quiet:
        raise click.BadOptionUsage(
            option_name="quiet",
            message="--debug and --quiet cannot be used at the same time.")

    if debug:
        click_config.log_to_sentry = False
        click_config.log_to_file = True
        globalLogPublisher.removeObserver(logToSentry)  # Sentry
        GlobalConsoleLogger.set_log_level("debug")

    elif quiet:
        globalLogPublisher.removeObserver(logToSentry)
        globalLogPublisher.removeObserver(SimpleObserver)
        globalLogPublisher.removeObserver(getJsonFileObserver())

    #
    # Pre-Launch Warnings
    #
    if not quiet:
        if dev:
            click.secho("WARNING: Running in development mode", fg='yellow')
        if force:
            click.secho("WARNING: Force is enabled", fg='yellow')

    #
    # Unauthenticated Configurations
    #
    if action == "init":
        """Create a brand-new persistent Ursula"""

        if dev and not quiet:
            click.secho("WARNING: Using temporary storage area", fg='yellow')

        if not config_root:  # Flag
            config_root = click_config.config_file  # Envvar

        if not rest_host:
            rest_host = click.prompt(
                "Enter Ursula's public-facing IPv4 address")

        ursula_config = UrsulaConfiguration.generate(
            password=click_config.get_password(confirm=True),
            config_root=config_root,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath,
            domains={network} if network else None,
            federated_only=federated_only,
            checksum_public_address=checksum_address,
            no_registry=federated_only or no_registry,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            poa=poa)

        if not quiet:
            click.secho("Generated keyring {}".format(
                ursula_config.keyring_dir),
                        fg='green')
            click.secho("Saved configuration file {}".format(
                ursula_config.config_file_location),
                        fg='green')

            # Give the use a suggestion as to what to do next...
            how_to_run_message = "\nTo run an Ursula node from the default configuration filepath run: \n\n'{}'\n"
            suggested_command = 'nucypher ursula run'
            if config_root is not None:
                config_file_location = os.path.join(
                    config_root, config_file
                    or UrsulaConfiguration.CONFIG_FILENAME)
                suggested_command += ' --config-file {}'.format(
                    config_file_location)
            click.secho(how_to_run_message.format(suggested_command),
                        fg='green')
            return  # FIN

        else:
            click.secho("OK")

    elif action == "destroy":
        """Delete all configuration files from the disk"""

        if dev:
            message = "'nucypher ursula destroy' cannot be used in --dev mode"
            raise click.BadOptionUsage(option_name='--dev', message=message)

        destroy_system_configuration(config_class=UrsulaConfiguration,
                                     config_file=config_file,
                                     network=network,
                                     config_root=config_root,
                                     force=force,
                                     log=log)
        if not quiet:
            click.secho("Destroyed {}".format(config_root))
        return

    # Development Configuration
    if dev:
        ursula_config = UrsulaConfiguration(
            dev_mode=True,
            domains={TEMPORARY_DOMAIN},
            poa=poa,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            checksum_public_address=checksum_address,
            federated_only=federated_only,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath)
    # Authenticated Configurations
    else:

        # Deserialize network domain name if override passed
        if network:
            domain_constant = getattr(constants, network.upper())
            domains = {domain_constant}
        else:
            domains = None

        ursula_config = UrsulaConfiguration.from_configuration_file(
            filepath=config_file,
            domains=domains,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath,

            # TODO: Handle Boolean overrides
            # poa=poa,
            # federated_only=federated_only,
        )

        try:  # Unlock Keyring
            if not quiet:
                click.secho('Decrypting keyring...', fg='blue')
            ursula_config.keyring.unlock(password=click_config.get_password()
                                         )  # Takes ~3 seconds, ~1GB Ram
        except CryptoError:
            raise ursula_config.keyring.AuthenticationFailed

    if not ursula_config.federated_only:
        try:
            ursula_config.connect_to_blockchain(recompile_contracts=False)
            ursula_config.connect_to_contracts()
        except EthereumContractRegistry.NoRegistry:
            message = "Cannot configure blockchain character: No contract registry found; " \
                      "Did you mean to pass --federated-only?"
            raise EthereumContractRegistry.NoRegistry(message)

    click_config.ursula_config = ursula_config  # Pass Ursula's config onto staking sub-command

    #
    # Launch Warnings
    #
    if not quiet:
        if ursula_config.federated_only:
            click.secho("WARNING: Running in Federated mode", fg='yellow')

    #
    # Action Switch
    #
    if action == 'run':
        """Seed, Produce, Run!"""

        #
        # Seed - Step 1
        #
        teacher_nodes = list()
        if teacher_uri:
            node = Ursula.from_teacher_uri(
                teacher_uri=teacher_uri,
                min_stake=min_stake,
                federated_only=ursula_config.federated_only)
            teacher_nodes.append(node)

        #
        # Produce - Step 2
        #
        ursula = ursula_config(known_nodes=teacher_nodes, lonely=lonely)

        # GO!
        try:

            #
            # Run - Step 3
            #
            click.secho("Connecting to {}".format(','.join(
                str(d) for d in ursula_config.domains)),
                        fg='blue',
                        bold=True)
            click.secho("Running Ursula {} on {}".format(
                ursula, ursula.rest_interface),
                        fg='green',
                        bold=True)
            if not debug:
                stdio.StandardIO(UrsulaCommandProtocol(ursula=ursula))

            if dry_run:
                # That's all folks!
                return

            ursula.get_deployer().run()  # <--- Blocking Call (Reactor)

        except Exception as e:
            ursula_config.log.critical(str(e))
            click.secho("{} {}".format(e.__class__.__name__, str(e)), fg='red')
            raise  # Crash :-(

        finally:
            if not quiet:
                click.secho("Stopping Ursula")
            ursula_config.cleanup()
            if not quiet:
                click.secho("Ursula Stopped", fg='red')

        return

    elif action == "save-metadata":
        """Manually save a node self-metadata file"""

        ursula = ursula_config.produce(ursula_config=ursula_config)
        metadata_path = ursula.write_node_metadata(node=ursula)
        if not quiet:
            click.secho("Successfully saved node metadata to {}.".format(
                metadata_path),
                        fg='green')
        return

    elif action == "view":
        """Paint an existing configuration to the console"""
        paint_configuration(
            config_filepath=config_file or ursula_config.config_file_location)
        return

    elif action == "forget":
        """Forget all known nodes via storages"""
        click.confirm("Permanently delete all known node data?", abort=True)
        ursula_config.forget_nodes()
        message = "Removed all stored node node metadata and certificates"
        click.secho(message=message, fg='red')
        return

    else:
        raise click.BadArgumentUsage("No such argument {}".format(action))
Exemple #12
0
def ursula(
        click_config,
        action,
        dev,
        quiet,
        dry_run,
        force,
        lonely,
        network,
        teacher_uri,
        min_stake,
        rest_host,
        rest_port,
        db_filepath,
        checksum_address,
        federated_only,
        poa,
        config_root,
        config_file,
        metadata_dir,  # TODO: Start nodes from an additional existing metadata dir
        provider_uri,
        recompile_solidity,
        no_registry,
        registry_filepath) -> None:
    """
    Manage and run an "Ursula" PRE node.

    \b
    Actions
    -------------------------------------------------
    \b
    run            Run an "Ursula" node.
    init           Create a new Ursula node configuration.
    view           View the Ursula node's configuration.
    forget         Forget all known nodes.
    save-metadata  Manually write node metadata to disk without running
    destroy        Delete Ursula node configuration.

    """

    #
    # Boring Setup Stuff
    #
    if not quiet:
        log = Logger('ursula.cli')

    if click_config.debug and quiet:
        raise click.BadOptionUsage(
            option_name="quiet",
            message="--debug and --quiet cannot be used at the same time.")

    if click_config.debug:
        click_config.log_to_sentry = False
        click_config.log_to_file = True
        globalLogPublisher.removeObserver(logToSentry)  # Sentry
        GlobalConsoleLogger.set_log_level("debug")

    elif quiet:
        globalLogPublisher.removeObserver(logToSentry)
        globalLogPublisher.removeObserver(SimpleObserver)
        globalLogPublisher.removeObserver(getJsonFileObserver())

    if not click_config.json_ipc and not click_config.quiet:
        click.secho(URSULA_BANNER)

    #
    # Pre-Launch Warnings
    #
    if not quiet:
        if dev:
            click.secho("WARNING: Running in development mode", fg='yellow')
        if force:
            click.secho("WARNING: Force is enabled", fg='yellow')

    #
    # Unauthenticated Configurations
    #
    if action == "init":
        """Create a brand-new persistent Ursula"""

        if not network:
            raise click.BadArgumentUsage(
                '--network is required to initialize a new configuration.')

        if dev:
            actions.handle_control_output(
                message="WARNING: Using temporary storage area",
                color='yellow',
                quiet=quiet,
                json=click_config.json)

        if not config_root:  # Flag
            config_root = click_config.config_file  # Envvar

        if not rest_host:
            rest_host = click.prompt(
                "Enter Ursula's public-facing IPv4 address"
            )  # TODO: Remove this step

        ursula_config = UrsulaConfiguration.generate(
            password=click_config.get_password(confirm=True),
            config_root=config_root,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath,
            domains={network} if network else None,
            federated_only=federated_only,
            checksum_public_address=checksum_address,
            no_registry=federated_only or no_registry,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            poa=poa)

        click_config.emitter(message="Generated keyring {}".format(
            ursula_config.keyring_dir),
                             color='green')

        click_config.emitter(message="Saved configuration file {}".format(
            ursula_config.config_file_location),
                             color='green')

        # Give the use a suggestion as to what to do next...
        how_to_run_message = "\nTo run an Ursula node from the default configuration filepath run: \n\n'{}'\n"
        suggested_command = 'nucypher ursula run'
        if config_root is not None:
            config_file_location = os.path.join(
                config_root, config_file
                or UrsulaConfiguration.CONFIG_FILENAME)
            suggested_command += ' --config-file {}'.format(
                config_file_location)

        return click_config.emitter(
            message=how_to_run_message.format(suggested_command),
            color='green')

    # Development Configuration
    if dev:
        ursula_config = UrsulaConfiguration(
            dev_mode=True,
            domains={TEMPORARY_DOMAIN},
            poa=poa,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            checksum_public_address=checksum_address,
            federated_only=federated_only,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath)
    # Authenticated Configurations
    else:

        # Deserialize network domain name if override passed
        if network:
            domain_constant = getattr(constants, network.upper())
            domains = {domain_constant}
        else:
            domains = None

        ursula_config = UrsulaConfiguration.from_configuration_file(
            filepath=config_file,
            domains=domains,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            rest_host=rest_host,
            rest_port=rest_port,
            db_filepath=db_filepath,

            # TODO: Handle Boolean overrides
            # poa=poa,
            # federated_only=federated_only,
        )

        actions.unlock_keyring(configuration=ursula_config,
                               password=click_config.get_password())

    if not ursula_config.federated_only:
        actions.connect_to_blockchain(configuration=ursula_config,
                                      recompile_contracts=recompile_solidity)

    click_config.ursula_config = ursula_config  # Pass Ursula's config onto staking sub-command

    #
    # Launch Warnings
    #

    if ursula_config.federated_only:
        click_config.emitter(message="WARNING: Running in Federated mode",
                             color='yellow')
    #
    # Action Switch
    #
    if action == 'run':
        """Seed, Produce, Run!"""

        #
        # Seed - Step 1
        #
        teacher_uris = [teacher_uri] if teacher_uri else list()
        teacher_nodes = actions.load_seednodes(
            teacher_uris=teacher_uris,
            min_stake=min_stake,
            federated_only=federated_only,
            network_middleware=click_config.middleware)

        #
        # Produce - Step 2
        #
        URSULA = ursula_config(known_nodes=teacher_nodes, lonely=lonely)

        # GO!
        try:

            #
            # Run - Step 3
            #
            click_config.emitter(message="Connecting to {}".format(','.join(
                str(d) for d in ursula_config.domains)),
                                 color='green',
                                 bold=True)

            click_config.emitter(message="Running Ursula {} on {}".format(
                URSULA, URSULA.rest_interface),
                                 color='green',
                                 bold=True)

            if not click_config.debug:
                stdio.StandardIO(UrsulaCommandProtocol(ursula=URSULA))

            if dry_run:
                # That's all folks!
                return

            URSULA.get_deployer().run()  # <--- Blocking Call (Reactor)

        except Exception as e:
            ursula_config.log.critical(str(e))
            click_config.emitter(message="{} {}".format(
                e.__class__.__name__, str(e)),
                                 color='red',
                                 bold=True)
            raise  # Crash :-(

        finally:
            click_config.emitter(message="Stopping Ursula", color='green')
            ursula_config.cleanup()
            click_config.emitter(message="Ursula Stopped", color='red')
        return

    elif action == "save-metadata":
        """Manually save a node self-metadata file"""

        URSULA = ursula_config.produce(ursula_config=ursula_config)
        metadata_path = ursula.write_node_metadata(node=URSULA)
        return click_config.emitter(
            message="Successfully saved node metadata to {}.".format(
                metadata_path),
            color='green')

    elif action == "view":
        """Paint an existing configuration to the console"""
        response = UrsulaConfiguration._read_configuration_file(
            filepath=config_file or ursula_config.config_file_location)
        return click_config.emitter(response=response)

    elif action == "forget":
        # TODO: Move to character control
        actions.forget(configuration=ursula_config)
        return

    elif action == "destroy":
        """Delete all configuration files from the disk"""

        if dev:
            message = "'nucypher ursula destroy' cannot be used in --dev mode"
            raise click.BadOptionUsage(option_name='--dev', message=message)

        destroyed_filepath = destroy_system_configuration(
            config_class=UrsulaConfiguration,
            config_file=config_file,
            network=network,
            config_root=ursula_config.config_file_location,
            force=force)

        return click_config.emitter(message=f"Destroyed {destroyed_filepath}",
                                    color='green')

    else:
        raise click.BadArgumentUsage("No such argument {}".format(action))