Ejemplo n.º 1
0
    def create_character(self, emitter, config_file, debug):

        felix_config = self.config_options.create_config(emitter, config_file)

        try:
            # Authenticate
            unlock_nucypher_keyring(
                emitter,
                character_configuration=felix_config,
                password=get_nucypher_password(confirm=False))

            # Produce Teacher Ursulas
            teacher_nodes = actions.load_seednodes(
                emitter,
                teacher_uris=self.teacher_uris,
                min_stake=self.min_stake,
                federated_only=felix_config.federated_only,
                network_domains=felix_config.domains,
                network_middleware=self.middleware)

            # Produce Felix
            FELIX = felix_config.produce(domains=self.config_options.domains,
                                         known_nodes=teacher_nodes)
            FELIX.make_web_app(
            )  # attach web application, but dont start service

            return FELIX
        except Exception as e:
            if debug:
                raise
            else:
                emitter.echo(str(e), color='red', bold=True)
                raise click.Abort
Ejemplo n.º 2
0
def moe(click_config, teacher_uri, min_stake, network, ws_port, dry_run, http_port, learn_on_launch):
    """
    "Moe the Monitor" management commands.
    """

    emitter = click_config.emitter

    # Banner
    emitter.clear()
    emitter.banner(MOE_BANNER)

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else None
    teacher_nodes = actions.load_seednodes(emitter,
                                           teacher_uris=teacher_uris,
                                           min_stake=min_stake,
                                           federated_only=True,    # TODO: hardcoded for now.  Is Moe a Character?
                                           network_domains={network} if network else None,
                                           network_middleware=click_config.middleware)

    # Make Moe
    MOE = Moe(domains={network} if network else None,
              network_middleware=RestMiddleware(),
              known_nodes=teacher_nodes,
              federated_only=True)

    # Run
    MOE.start_learning_loop(now=learn_on_launch)

    emitter.message(f"Running Moe on 127.0.0.1:{http_port}")

    MOE.start(http_port=http_port, ws_port=ws_port, dry_run=dry_run)
Ejemplo n.º 3
0
def create_crawler(node_db_filepath: str = IN_MEMORY_FILEPATH,
                   dont_set_teacher: bool = False):
    registry = InMemoryContractRegistry()
    middleware = RestMiddleware()
    teacher_nodes = None
    if not dont_set_teacher:
        teacher_nodes = actions.load_seednodes(
            None,  # TODO: Needs emitter
            teacher_uris=[DEFAULT_TEACHER],  # TODO: Needs Cleanup
            min_stake=0,
            federated_only=False,
            network_domains={'gemini'},  # TODO: Needs Cleanup
            network_middleware=middleware)

    crawler = Crawler(
        domains={'goerli'},  # TODO: Needs Cleanup
        network_middleware=middleware,
        known_nodes=teacher_nodes,
        registry=registry,
        start_learning_now=True,
        learn_on_same_thread=False,
        influx_host='localhost',  # TODO: Needs Cleanup
        influx_port=8086,  # TODO: Needs Cleanup
        node_storage_filepath=node_db_filepath)
    return crawler
Ejemplo n.º 4
0
def crawl(click_config, teacher_uri, registry_filepath, min_stake, network,
          learn_on_launch, provider_uri, influx_host, influx_port, dry_run):
    """
    Gather NuCypher network information.
    """

    # Banner
    emitter = click_config.emitter
    emitter.clear()
    emitter.banner(MONITOR_BANNER.format(CRAWLER))

    registry = _get_registry(provider_uri, registry_filepath)

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else None
    teacher_nodes = actions.load_seednodes(
        emitter,
        teacher_uris=teacher_uris,
        min_stake=min_stake,
        federated_only=False,
        network_domains={network} if network else None,
        network_middleware=click_config.middleware)

    # Configure Storage
    crawler = Crawler(domains={network} if network else None,
                      network_middleware=RestMiddleware(),
                      known_nodes=teacher_nodes,
                      registry=registry,
                      start_learning_now=True,
                      learn_on_same_thread=learn_on_launch,
                      blockchain_db_host=influx_host,
                      blockchain_db_port=influx_port)
    if not dry_run:
        crawler.start()
        reactor.run()
Ejemplo n.º 5
0
def moe(click_config, teacher_uri, min_stake, network, ws_port, dry_run, http_port, learn_on_launch):

    """
    "Moe" NuCypher node monitor CLI.
    """

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

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else list()
    teacher_nodes = actions.load_seednodes(teacher_uris=teacher_uris,
                                           min_stake=min_stake,
                                           federated_only=True,    # TODO: hardcoded for now
                                           network_middleware=click_config.middleware)

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

    monitor = Moe(
        domains=domains,
        network_middleware=RestMiddleware(),
        known_nodes=teacher_nodes,
        federated_only=True)

    monitor.start_learning_loop(now=learn_on_launch)
    monitor.start(http_port=http_port, ws_port=ws_port, dry_run=dry_run)
Ejemplo n.º 6
0
def crawl(
    general_config,
    teacher_uri,
    registry_filepath,
    min_stake,
    network,
    learn_on_launch,
    provider_uri,
    influx_host,
    influx_port,
    http_port,
    dry_run,
    eager,
    poa,
):
    """
    Gather NuCypher network information.
    """

    # Banner
    emitter = general_config.emitter
    emitter.clear()
    emitter.banner(MONITOR_BANNER.format(CRAWLER))

    # Setup
    BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri,
                                                    poa=poa)
    registry = _get_registry(registry_filepath, network)

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else None
    teacher_nodes = actions.load_seednodes(
        emitter,
        teacher_uris=teacher_uris,
        min_stake=min_stake,
        federated_only=False,
        network_domains={network} if network else None,
        network_middleware=RestMiddleware())

    crawler = Crawler(domains={network} if network else None,
                      network_middleware=RestMiddleware(),
                      known_nodes=teacher_nodes,
                      registry=registry,
                      start_learning_now=eager,
                      learn_on_same_thread=learn_on_launch,
                      influx_host=influx_host,
                      influx_port=influx_port)

    emitter.message(f"Network: {network.capitalize()}", color='blue')
    emitter.message(f"InfluxDB: {influx_host}:{influx_port}", color='blue')
    emitter.message(f"Provider: {provider_uri}", color='blue')
    emitter.message(f"Refresh Rate: {crawler._refresh_rate}s", color='blue')
    message = f"Running Nucypher Crawler JSON endpoint at http://localhost:{http_port}/stats"
    emitter.message(message, color='green', bold=True)
    if not dry_run:
        crawler.start(eager=eager)
        reactor.run()
Ejemplo n.º 7
0
def create_crawler(node_db_filepath: str = IN_MEMORY_FILEPATH,
                   dont_set_teacher: bool = False):
    registry = InMemoryContractRegistry()
    middleware = RestMiddleware()
    teacher_nodes = None
    if not dont_set_teacher:
        teacher_nodes = actions.load_seednodes(
            None,
            teacher_uris=['https://discover.nucypher.network:9151'],
            min_stake=0,
            federated_only=False,
            network_domains={'goerli'},
            network_middleware=middleware)

    crawler = Crawler(domains={'goerli'},
                      network_middleware=middleware,
                      known_nodes=teacher_nodes,
                      registry=registry,
                      start_learning_now=True,
                      learn_on_same_thread=False,
                      blockchain_db_host='localhost',
                      blockchain_db_port=8086,
                      node_storage_filepath=node_db_filepath)
    return crawler
Ejemplo n.º 8
0
def felix(click_config, action, teacher_uri, enode, min_stake, network, host,
          dry_run, port, discovery_port, provider_uri, geth, config_root,
          checksum_address, poa, config_file, db_filepath, no_registry,
          registry_filepath, dev, force):

    # Intro
    click.clear()
    if not click_config.quiet:
        click.secho(FELIX_BANNER.format(checksum_address or ''))

    ETH_NODE = NO_BLOCKCHAIN_CONNECTION
    if geth:
        ETH_NODE = actions.get_provider_process(dev=dev)
        provider_uri = ETH_NODE.provider_uri

    if action == "init":
        """Create a brand-new Felix"""

        if not config_root:  # Flag
            config_root = DEFAULT_CONFIG_ROOT  # Envvar or init-only default

        # Acquire Keyring Password
        new_password = click_config.get_password(confirm=True)

        try:
            new_felix_config = FelixConfiguration.generate(
                password=new_password,
                config_root=config_root,
                rest_host=host,
                rest_port=discovery_port,
                db_filepath=db_filepath,
                domains={network} if network else None,
                checksum_address=checksum_address,
                download_registry=not no_registry,
                registry_filepath=registry_filepath,
                provider_uri=provider_uri,
                provider_process=ETH_NODE,
                poa=poa)
        except Exception as e:
            if click_config.debug:
                raise
            else:
                click.secho(str(e), fg='red', bold=True)
                raise click.Abort

        # Paint Help
        painting.paint_new_installation_help(
            new_configuration=new_felix_config,
            config_root=config_root,
            config_file=config_file)

        return  # <-- do not remove (conditional flow control)

    # Domains -> bytes | or default
    domains = [network] if network else None

    # Load Felix from Configuration File with overrides
    try:
        felix_config = FelixConfiguration.from_configuration_file(
            filepath=config_file,
            domains=domains,
            registry_filepath=registry_filepath,
            provider_process=ETH_NODE,
            provider_uri=provider_uri,
            rest_host=host,
            rest_port=port,
            db_filepath=db_filepath,
            poa=poa)

    except FileNotFoundError:
        click.secho(
            f"No Felix configuration file found at {config_file}. "
            f"Check the filepath or run 'nucypher felix init' to create a new system configuration."
        )
        raise click.Abort

    try:

        # Connect to Blockchain
        felix_config.connect_to_blockchain()

        # Authenticate
        password = click_config.get_password(confirm=False)
        click_config.unlock_keyring(character_configuration=felix_config,
                                    password=password)

        # Produce Teacher Ursulas
        teacher_nodes = actions.load_seednodes(
            teacher_uris=[teacher_uri] if teacher_uri else None,
            min_stake=min_stake,
            federated_only=felix_config.federated_only,
            network_domains=felix_config.domains,
            network_middleware=click_config.middleware)

        # Add ETH Bootnode or Peer
        if enode:
            if geth:
                felix_config.blockchain.interface.w3.geth.admin.addPeer(enode)
                click.secho(f"Added ethereum peer {enode}")
            else:
                raise NotImplemented  # TODO: other backends

        # Produce Felix
        FELIX = felix_config.produce(domains=network,
                                     known_nodes=teacher_nodes)
        FELIX.make_web_app()  # attach web application, but dont start service

    except Exception as e:
        if click_config.debug:
            raise
        else:
            click.secho(str(e), fg='red', bold=True)
            raise click.Abort

    if action == "createdb":  # Initialize Database
        if os.path.isfile(FELIX.db_filepath):
            if not force:
                click.confirm("Overwrite existing database?", abort=True)
            os.remove(FELIX.db_filepath)
            click.secho(f"Destroyed existing database {FELIX.db_filepath}")

        FELIX.create_tables()
        click.secho(f"\nCreated new database at {FELIX.db_filepath}",
                    fg='green')

    elif action == 'view':
        token_balance = FELIX.token_balance
        eth_balance = FELIX.eth_balance
        click.secho(f"""
Address .... {FELIX.checksum_address}
NU ......... {str(token_balance)}
ETH ........ {str(eth_balance)}
        """)

    elif action == "accounts":
        accounts = FELIX.blockchain.interface.w3.eth.accounts
        for account in accounts:
            click.secho(account)

    elif action == "destroy":
        """Delete all configuration files from the disk"""
        actions.destroy_configuration(character_config=felix_config,
                                      force=force)

    elif action == 'run':  # Start web services

        try:
            click.secho("Waiting for blockchain sync...", fg='yellow')
            FELIX.blockchain.sync()
            FELIX.start(host=host,
                        port=port,
                        web_services=not dry_run,
                        distribution=True,
                        crash_on_error=click_config.debug)
        finally:
            FELIX.blockchain.disconnect()

    else:
        raise click.BadArgumentUsage("No such argument {}".format(action))
Ejemplo n.º 9
0
def bob(click_config, action, quiet, teacher_uri, min_stake, http_port,
        discovery_port, federated_only, network, config_root, config_file,
        provider_uri, registry_filepath, dev, force, dry_run, label,
        policy_encrypting_key, alice_verifying_key, message_kit):
    """
    Start and manage a "Bob" character.
    """

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

    if action == 'init':
        """Create a brand-new persistent Bob"""

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

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

        new_bob_config = BobConfiguration.generate(
            password=click_config._get_password(confirm=True),
            config_root=config_root or click_config,
            rest_host="localhost",
            domains={network} if network else None,
            federated_only=federated_only,
            no_registry=click_config.no_registry,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri)

        return painting.paint_new_installation_help(
            new_configuration=new_bob_config, config_file=config_file)

    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_path = actions.destroy_system_configuration(
            config_class=BobConfiguration,
            config_file=config_file,
            network=network,
            config_root=config_root,
            force=force)

        return click_config.emitter(message=f"Destroyed {destroyed_path}")

    #
    # Get Bob Configuration
    #

    if dev:
        bob_config = BobConfiguration(
            dev_mode=True,
            domains={network},
            provider_uri=provider_uri,
            federated_only=True,
            network_middleware=click_config.middleware)
    else:
        bob_config = BobConfiguration.from_configuration_file(
            filepath=config_file,
            domains={network or GLOBAL_DOMAIN},
            rest_port=discovery_port,
            provider_uri=provider_uri,
            network_middleware=click_config.middleware)

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else list()
    teacher_nodes = actions.load_seednodes(
        teacher_uris=teacher_uris,
        min_stake=min_stake,
        federated_only=federated_only,
        network_middleware=click_config.middleware)

    if not dev:
        click_config.unlock_keyring(character_configuration=bob_config)

    # Produce
    BOB = bob_config(known_nodes=teacher_nodes,
                     network_middleware=click_config.middleware)

    # Switch to character control emitter
    if click_config.json_ipc:
        BOB.controller.emitter = IPCStdoutEmitter(quiet=click_config.quiet)

    if action == "run":
        click_config.emitter(
            message=f"Bob Verifying Key {bytes(BOB.stamp).hex()}",
            color='green',
            bold=True)
        bob_encrypting_key = bytes(BOB.public_keys(DecryptingPower)).hex()
        click_config.emitter(
            message=f"Bob Encrypting Key {bob_encrypting_key}",
            color="blue",
            bold=True)
        controller = BOB.make_web_controller()
        BOB.log.info('Starting HTTP Character Web Controller')
        return controller.start(http_port=http_port, dry_run=dry_run)

    elif action == "view":
        """Paint an existing configuration to the console"""
        response = BobConfiguration._read_configuration_file(
            filepath=config_file or bob_config.config_file_location)
        return BOB.controller.emitter(response=response)

    elif action == "public-keys":
        response = BOB.controller.public_keys()
        return response

    elif action == "retrieve":

        if not all(
            (label, policy_encrypting_key, alice_verifying_key, message_kit)):
            input_specification, output_specification = BOB.control.get_specifications(
                interface_name='retrieve')
            required_fields = ', '.join(input_specification)
            raise click.BadArgumentUsage(
                f'{required_fields} are required flags to retrieve')

        bob_request_data = {
            'label': label,
            'policy_encrypting_key': policy_encrypting_key,
            'alice_verifying_key': alice_verifying_key,
            'message_kit': message_kit,
        }

        response = BOB.controller.retrieve(request=bob_request_data)
        return response

    else:
        raise click.BadArgumentUsage(f"No such argument {action}")
Ejemplo n.º 10
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,
           withdraw_address,
           federated_only,
           poa,
           config_root,
           config_file,
           provider_uri,
           recompile_solidity,
           no_registry,
           registry_filepath,
           value,
           duration,
           index,
           list_,
           divide
           ) -> None:
    """
    Manage and run an "Ursula" PRE node.

    \b
    Actions
    -------------------------------------------------
    \b
    init              Create a new Ursula node configuration.
    view              View the Ursula node's configuration.
    run               Run an "Ursula" node.
    save-metadata     Manually write node metadata to disk without running
    forget            Forget all known nodes.
    destroy           Delete Ursula node configuration.
    stake             Manage stakes for this node.
    confirm-activity  Manually confirm-activity for the current period.
    collect-reward    Withdraw staking reward.

    """

    #
    # 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 not click_config.json_ipc and not click_config.quiet:
        click.secho(URSULA_BANNER.format(checksum_address or ''))

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

    #
    # Unauthenticated Configurations & Un-configured Ursula Control
    #
    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:
            click_config.emitter(message="WARNING: Using temporary storage area", color='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")  # 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)

        painting.paint_new_installation_help(new_configuration=ursula_config,
                                             config_root=config_root,
                                             config_file=config_file,
                                             federated_only=federated_only)
        return

    #
    # Configured Ursulas
    #

    # 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:

        # Domains -> bytes | or default
        domains = [bytes(network, encoding='utf-8')] if network else None

        # Load Ursula from Configuration File
        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,
                                                                    poa=poa)

        click_config.unlock_keyring(character_configuration=ursula_config)

    #
    # Connect to Blockchain (Non-Federated)
    #

    if not ursula_config.federated_only:
        click_config.connect_to_blockchain(character_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')

    # 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)

    #
    # Action Switch
    #

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

        # GO!
        try:

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

            # Ursula Deploy Warnings
            click_config.emitter(
                message="Connecting to {}".format(','.join(str(d, encoding='utf-8') for d in ursula_config.domains)),
                color='green',
                bold=True)

            if not URSULA.federated_only and URSULA.stakes:
                click_config.emitter(
                    message=f"Staking {str(URSULA.total_staked)} ~ Keep Ursula Online!",
                    color='blue',
                    bold=True)

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

            if dry_run:
                return  # <-- ABORT -X (Last Chance)

            # Run - Step 3
            node_deployer = URSULA.get_deployer()
            node_deployer.addServices()
            node_deployer.catalogServers(node_deployer.hendrix)
            node_deployer.run()   # <--- Blocking Call (Reactor)

        # Handle Crash
        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 :-(

        # Graceful Exit / 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"""
        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":
        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')

    elif action == 'stake':

        # List Only
        if list_:
            if not URSULA.stakes:
                click.echo(f"There are no existing stakes for {URSULA.checksum_public_address}")
            painting.paint_stakes(stakes=URSULA.stakes)
            return

        # Divide Only
        if divide:
            """Divide an existing stake by specifying the new target value and end period"""

            # Validate
            if len(URSULA.stakes) == 0:
                click.secho("There are no active stakes for {}".format(URSULA.checksum_public_address))
                return

            # Selection
            if index is None:
                painting.paint_stakes(stakes=URSULA.stakes)
                index = click.prompt("Select a stake to divide", type=click.IntRange(min=0, max=len(URSULA.stakes)-1))

            # Lookup the stake
            current_stake = URSULA.stakes[index]

            # Value
            if not value:
                value = click.prompt(f"Enter target value (must be less than {str(current_stake.value)})", type=STAKE_VALUE)
            value = NU(value, 'NU')

            # Duration
            if not duration:
                extension = click.prompt("Enter number of periods to extend", type=STAKE_EXTENSION)
            else:
                extension = duration

            if not force:
                painting.paint_staged_stake_division(ursula=URSULA,
                                                     original_index=index,
                                                     original_stake=current_stake,
                                                     target_value=value,
                                                     extension=extension)

                click.confirm("Is this correct?", abort=True)

            txhash_bytes = URSULA.divide_stake(stake_index=index,
                                               target_value=value,
                                               additional_periods=extension)

            if not quiet:
                click.secho('Successfully divided stake', fg='green')
                click.secho(f'Transaction Hash ........... {txhash_bytes.hex()}')

            # Show the resulting stake list
            painting.paint_stakes(stakes=URSULA.stakes)

            return

        # Confirm new stake init
        if not force:
            click.confirm("Stage a new stake?", abort=True)

        # Validate balance
        balance = URSULA.token_balance
        if balance == 0:
            click.secho(f"{ursula.checksum_public_address} has 0 NU.")
            raise click.Abort
        if not quiet:
            click.echo(f"Current balance: {balance}")

        # Gather stake value
        if not value:
            value = click.prompt(f"Enter stake value", type=STAKE_VALUE, default=NU(MIN_ALLOWED_LOCKED, 'NuNit'))
        else:
            value = NU(int(value), 'NU')

        # Duration
        if not quiet:
            message = "Minimum duration: {} | Maximum Duration: {}".format(MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS)
            click.echo(message)
        if not duration:
            duration = click.prompt("Enter stake duration in periods (1 Period = 24 Hours)", type=STAKE_DURATION)
        start_period = URSULA.miner_agent.get_current_period()
        end_period = start_period + duration

        # Review
        if not force:
            painting.paint_staged_stake(ursula=URSULA,
                                        stake_value=value,
                                        duration=duration,
                                        start_period=start_period,
                                        end_period=end_period)

            if not dev:
                actions.confirm_staged_stake(ursula=URSULA, value=value, duration=duration)

        # Last chance to bail
        if not force:
            click.confirm("Publish staged stake to the blockchain?", abort=True)

        staking_transactions = URSULA.initialize_stake(amount=int(value), lock_periods=duration)
        painting.paint_staking_confirmation(ursula=URSULA, transactions=staking_transactions)
        return

    elif action == 'confirm-activity':
        if not URSULA.stakes:
            click.secho("There are no active stakes for {}".format(URSULA.checksum_public_address))
            return
        URSULA.miner_agent.confirm_activity(node_address=URSULA.checksum_public_address)
        return

    elif action == 'collect-reward':
        """Withdraw staking reward to the specified wallet address"""
        if not force:
            click.confirm(f"Send {URSULA.calculate_reward()} to {URSULA.checksum_public_address}?")

        URSULA.collect_policy_reward(collector_address=withdraw_address or checksum_address)
        URSULA.collect_staking_reward()

    else:
        raise click.BadArgumentUsage("No such argument {}".format(action))
Ejemplo n.º 11
0
def alice(click_config, action, teacher_uri, min_stake, http_port,
          discovery_port, federated_only, network, config_root, config_file,
          provider_uri, no_registry, registry_filepath, dev, force, dry_run,
          bob_encrypting_key, bob_verifying_key, policy_encrypting_key, label,
          m, n):
    """
    Start and manage an "Alice" character.
    """

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

    if action == 'init':
        """Create a brand-new persistent Alice"""

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

        if dev:
            click_config.emitter(
                message="WARNING: Using temporary storage area",
                color='yellow')

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

        new_alice_config = AliceConfiguration.generate(
            password=click_config._get_password(confirm=True),
            config_root=config_root,
            rest_host="localhost",
            domains={network} if network else None,
            federated_only=federated_only,
            no_registry=no_registry,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri)

        return painting.paint_new_installation_help(
            new_configuration=new_alice_config,
            config_root=config_root,
            config_file=config_file)

    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_path = actions.destroy_system_configuration(
            config_class=AliceConfiguration,
            config_file=config_file,
            network=network,
            config_root=config_root,
            force=force)

        return nucypher_click_config.emitter(
            message=f"Destroyed {destroyed_path}", color='red')

    #
    # Get Alice Configuration
    #

    if dev:
        alice_config = AliceConfiguration(
            dev_mode=True,
            network_middleware=click_config.middleware,
            domains={network},
            provider_uri=provider_uri,
            federated_only=True)

    else:
        alice_config = AliceConfiguration.from_configuration_file(
            filepath=config_file,
            domains={network or GLOBAL_DOMAIN},
            network_middleware=click_config.middleware,
            rest_port=discovery_port,
            provider_uri=provider_uri)

    if not dev:
        click_config.unlock_keyring(character_configuration=alice_config)

    # Teacher Ursula
    teacher_uris = [teacher_uri] if teacher_uri else list()
    teacher_nodes = actions.load_seednodes(
        teacher_uris=teacher_uris,
        min_stake=min_stake,
        federated_only=federated_only,
        network_middleware=click_config.middleware)
    # Produce
    ALICE = alice_config(known_nodes=teacher_nodes,
                         network_middleware=click_config.middleware)

    # Switch to character control emitter
    if click_config.json_ipc:
        ALICE.controller.emitter = IPCStdoutEmitter(quiet=click_config.quiet)

    if action == "run":
        """Start Alice Web Controller"""
        ALICE.controller.emitter(
            message=f"Alice Verifying Key {bytes(ALICE.stamp).hex()}",
            color="green",
            bold=True)
        controller = ALICE.make_web_controller(
            crash_on_error=click_config.debug)
        ALICE.log.info('Starting HTTP Character Web Controller')
        return controller.start(http_port=http_port, dry_run=dry_run)

    elif action == "view":
        """Paint an existing configuration to the console"""
        configuration_file_location = config_file or alice_config.config_file_location
        response = AliceConfiguration._read_configuration_file(
            filepath=configuration_file_location)
        return ALICE.controller.emitter(
            response=response)  # TODO: Uses character control instead

    elif action == "public-keys":
        response = ALICE.controller.public_keys()
        return response

    elif action == "create-policy":
        if not all((bob_verifying_key, bob_encrypting_key, label)):
            raise click.BadArgumentUsage(
                message=
                "--bob-verifying-key, --bob-encrypting-key, and --label are "
                "required options to create a new policy.")

        create_policy_request = {
            'bob_encrypting_key': bob_encrypting_key,
            'bob_verifying_key': bob_verifying_key,
            'label': label,
            'm': m,
            'n': n,
        }

        return ALICE.controller.create_policy(request=create_policy_request)

    elif action == "derive-policy-pubkey":
        return ALICE.controller.derive_policy_encrypting_key(label=label)

    elif action == "grant":
        grant_request = {
            'bob_encrypting_key': bob_encrypting_key,
            'bob_verifying_key': bob_verifying_key,
            'label': label,
            'm': m,
            'n': n,
            'expiration':
            (maya.now() + datetime.timedelta(days=3)).iso8601(),  # TODO
        }

        return ALICE.controller.grant(request=grant_request)

    elif action == "revoke":
        return ALICE.controller.revoke(
            policy_encrypting_key=policy_encrypting_key)

    else:
        raise click.BadArgumentUsage(f"No such argument {action}")
Ejemplo n.º 12
0
def felix(click_config, action, teacher_uri, enode, min_stake, network, host,
          dry_run, port, discovery_port, provider_uri, geth, config_root,
          checksum_address, poa, config_file, db_filepath, registry_filepath,
          dev, force):
    """
    "Felix the Faucet" management commands.
    """

    emitter = click_config.emitter

    # Intro
    emitter.clear()
    emitter.banner(FELIX_BANNER.format(checksum_address or ''))

    ETH_NODE = NO_BLOCKCHAIN_CONNECTION
    if geth:
        ETH_NODE = actions.get_provider_process(dev=dev)
        provider_uri = ETH_NODE.provider_uri

    if action == "init":
        """Create a brand-new Felix"""

        if not config_root:  # Flag
            config_root = DEFAULT_CONFIG_ROOT  # Envvar or init-only default

        try:
            new_felix_config = FelixConfiguration.generate(
                password=get_nucypher_password(confirm=True),
                config_root=config_root,
                rest_host=host,
                rest_port=discovery_port,
                db_filepath=db_filepath,
                domains={network} if network else None,
                checksum_address=checksum_address,
                registry_filepath=registry_filepath,
                provider_uri=provider_uri,
                provider_process=ETH_NODE,
                poa=poa)
        except Exception as e:
            if click_config.debug:
                raise
            else:
                emitter.echo(str(e), color='red', bold=True)
                raise click.Abort

        # Paint Help
        painting.paint_new_installation_help(
            emitter, new_configuration=new_felix_config)

        return  # <-- do not remove (conditional flow control)

    # Domains -> bytes | or default
    domains = [network] if network else None

    # Load Felix from Configuration File with overrides
    try:
        felix_config = FelixConfiguration.from_configuration_file(
            filepath=config_file,
            domains=domains,
            registry_filepath=registry_filepath,
            provider_process=ETH_NODE,
            provider_uri=provider_uri,
            rest_host=host,
            rest_port=port,
            db_filepath=db_filepath,
            poa=poa)

    except FileNotFoundError:
        emitter.echo(
            f"No Felix configuration file found at {config_file}. "
            f"Check the filepath or run 'nucypher felix init' to create a new system configuration."
        )
        raise click.Abort

    if action == "destroy":
        """Delete all configuration files from the disk"""
        if dev:
            message = "'nucypher felix destroy' cannot be used in --dev mode"
            raise click.BadOptionUsage(option_name='--dev', message=message)
        actions.destroy_configuration(emitter,
                                      character_config=felix_config,
                                      force=force)
        return

    try:

        # Authenticate
        unlock_nucypher_keyring(emitter,
                                character_configuration=felix_config,
                                password=get_nucypher_password(confirm=False))

        # Produce Teacher Ursulas
        teacher_nodes = actions.load_seednodes(
            emitter,
            teacher_uris=[teacher_uri] if teacher_uri else None,
            min_stake=min_stake,
            federated_only=felix_config.federated_only,
            network_domains=felix_config.domains,
            network_middleware=click_config.middleware)

        # Produce Felix
        FELIX = felix_config.produce(domains=network,
                                     known_nodes=teacher_nodes)
        FELIX.make_web_app()  # attach web application, but dont start service

    except Exception as e:
        if click_config.debug:
            raise
        else:
            emitter.echo(str(e), color='red', bold=True)
            raise click.Abort

    if action == "createdb":  # Initialize Database
        if os.path.isfile(FELIX.db_filepath):
            if not force:
                click.confirm("Overwrite existing database?", abort=True)
            os.remove(FELIX.db_filepath)
            emitter.echo(f"Destroyed existing database {FELIX.db_filepath}")

        FELIX.create_tables()
        emitter.echo(f"\nCreated new database at {FELIX.db_filepath}",
                     color='green')

    elif action == 'view':
        token_balance = FELIX.token_balance
        eth_balance = FELIX.eth_balance
        emitter.echo(f"""
Address .... {FELIX.checksum_address}
NU ......... {str(token_balance)}
ETH ........ {str(eth_balance)}
        """)

    elif action == "accounts":
        accounts = FELIX.blockchain.client.accounts
        for account in accounts:
            emitter.echo(account)

    elif action == 'run':  # Start web services

        emitter.echo("Waiting for blockchain sync...", color='yellow')
        emitter.message(f"Running Felix on {host}:{port}")
        FELIX.start(host=host,
                    port=port,
                    web_services=not dry_run,
                    distribution=True,
                    crash_on_error=click_config.debug)

    else:
        raise click.BadArgumentUsage("No such argument {}".format(action))
Ejemplo n.º 13
0
def felix(click_config, action, teacher_uri, min_stake, network, host, dry_run,
          port, discovery_port, provider_uri, config_root, checksum_address,
          poa, config_file, db_filepath, no_registry, registry_filepath,
          force):

    if not click_config.quiet:
        click.secho(FELIX_BANNER.format(checksum_address or ''))

    if action == "init":
        """Create a brand-new Felix"""

        # Validate "Init" Input
        if not network:
            raise click.BadArgumentUsage(
                '--network is required to initialize a new configuration.')

        # Validate "Init" Input
        if not checksum_address:
            raise click.BadArgumentUsage(
                '--checksum-address is required to initialize a new Felix configuration.'
            )

        # Acquire Keyring Password
        if not config_root:  # Flag
            config_root = click_config.config_file  # Envvar
        new_password = click_config.get_password(confirm=True)

        new_felix_config = FelixConfiguration.generate(
            password=new_password,
            config_root=config_root,
            rest_host=host,
            rest_port=discovery_port,
            db_filepath=db_filepath,
            domains={network} if network else None,
            checksum_public_address=checksum_address,
            no_registry=no_registry,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            poa=poa)

        # Paint Help
        painting.paint_new_installation_help(
            new_configuration=new_felix_config,
            config_root=config_root,
            config_file=config_file)

        return  # <-- do not remove (conditional flow control)

    #
    # Authenticated Configurations
    #

    # Domains -> bytes | or default
    domains = [bytes(network, encoding='utf-8')] if network else None

    # Load Felix from Configuration File with overrides
    try:
        felix_config = FelixConfiguration.from_configuration_file(
            filepath=config_file,
            domains=domains,
            registry_filepath=registry_filepath,
            provider_uri=provider_uri,
            rest_host=host,
            rest_port=port,
            db_filepath=db_filepath,
            poa=poa)
    except FileNotFoundError:
        click.secho(
            f"No Felix configuration file found at {config_file}. "
            f"Check the filepath or run 'nucypher felix init' to create a new system configuration."
        )
        raise click.Abort

    else:

        # Produce Teacher Ursulas
        teacher_uris = [teacher_uri] if teacher_uri else list()
        teacher_nodes = actions.load_seednodes(
            teacher_uris=teacher_uris,
            min_stake=min_stake,
            federated_only=False,
            network_middleware=click_config.middleware)

        # Produce Felix
        click_config.unlock_keyring(character_configuration=felix_config)
        FELIX = felix_config.produce(domains=network,
                                     known_nodes=teacher_nodes)
        FELIX.make_web_app()  # attach web application, but dont start service

    if action == "createdb":  # Initialize Database
        if os.path.isfile(FELIX.db_filepath):
            if not force:
                click.confirm("Overwrite existing database?", abort=True)
            os.remove(FELIX.db_filepath)
            click.secho(f"Destroyed existing database {FELIX.db_filepath}")

        FELIX.create_tables()
        click.secho(f"Created new database at {FELIX.db_filepath}")

    elif action == 'view':
        token_balance = FELIX.token_balance
        eth_balance = FELIX.eth_balance
        click.secho(f"""
Address .... {FELIX.checksum_public_address}
NU ......... {str(token_balance)}
ETH ........ {str(eth_balance)}
        """)
        return

    elif action == "accounts":
        accounts = FELIX.blockchain.interface.w3.eth.accounts
        for account in accounts:
            click.secho(account)
        return

    elif action == 'run':  # Start web services
        FELIX.start(host=host,
                    port=port,
                    web_services=not dry_run,
                    distribution=True,
                    crash_on_error=click_config.debug)

    else:  # Error
        raise click.BadArgumentUsage("No such argument {}".format(action))
Ejemplo n.º 14
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))