Exemplo n.º 1
0
def select_stake(staker: Staker,
                 emitter: StdoutEmitter,
                 stakes_status: Stake.Status = Stake.Status.EDITABLE,
                 filter_function: Callable[[Stake], bool] = None) -> Stake:
    """Interactively select a stake or abort if there are no eligible stakes."""

    if stakes_status.is_child(Stake.Status.DIVISIBLE):
        emitter.echo(ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE, color='yellow')

    # Filter stakes by status
    stakes = staker.sorted_stakes(parent_status=stakes_status,
                                  filter_function=filter_function)
    if not stakes:
        emitter.echo(NO_STAKES_FOUND, color='red')
        raise click.Abort

    # Interactive Selection
    paint_unlocked = stakes_status.is_child(Stake.Status.UNLOCKED)
    paint_stakes(staker=staker,
                 emitter=emitter,
                 stakes=stakes,
                 paint_unlocked=paint_unlocked)
    indexed_stakes = {stake.index: stake for stake in stakes}
    indices = [str(index) for index in indexed_stakes.keys()]
    choice = click.prompt(SELECT_STAKE, type=click.Choice(indices))
    chosen_stake = indexed_stakes[int(choice)]
    return chosen_stake
Exemplo n.º 2
0
def perform_startup_ip_check(emitter: StdoutEmitter, ursula: Ursula, force: bool = False) -> None:
    """
    Used on ursula startup to determine if the external
    IP address is consistent with the configuration's values.
    """
    try:
        external_ip = determine_external_ip_address(network=ursula.domain, known_nodes=ursula.known_nodes)
    except UnknownIPAddress:
        message = 'Cannot automatically determine external IP address'
        emitter.message(message)
        return  # TODO: crash, or not to crash... that is the question
    rest_host = ursula.rest_interface.host
    try:
        validate_operator_ip(ip=rest_host)
    except InvalidOperatorIP:
        message = f'{rest_host} is not a valid or permitted operator IP address.  Set the correct external IP then try again\n' \
                  f'automatic configuration -> nucypher ursula config ip-address\n' \
                  f'manual configuration    -> nucypher ursula config --rest-host <IP ADDRESS>'
        emitter.message(message)
        return

    ip_mismatch = external_ip != rest_host
    if ip_mismatch and not force:
        error = f'\nx External IP address ({external_ip}) does not match configuration ({ursula.rest_interface.host}).\n'
        hint = f"Run 'nucypher ursula config ip-address' to reconfigure the IP address then try " \
               f"again or use --no-ip-checkup to bypass this check (not recommended).\n"
        emitter.message(error, color='red')
        emitter.message(hint, color='yellow')
        raise click.Abort()
    else:
        emitter.message('✓ External IP matches configuration', 'green')
Exemplo n.º 3
0
def confirm_enable_winding_down(emitter: StdoutEmitter,
                                staking_address: str) -> bool:
    """Interactively confirm enabling of winding down with user agreements."""
    emitter.message(WINDING_DOWN_AGREEMENT)
    click.confirm(
        CONFIRM_ENABLE_WINDING_DOWN.format(staking_address=staking_address),
        abort=True)
    return True
Exemplo n.º 4
0
def delete(force, card_id):
    """Delete an existing character card."""
    emitter = StdoutEmitter()
    card = select_card(emitter=emitter, card_identifier=card_id)
    if not force:
        click.confirm(f'Are you sure you want to delete {card}?', abort=True)
    card.delete()
    emitter.message(f'Deleted card for {card.id.hex()}.', color='red')
Exemplo n.º 5
0
def confirm_disable_snapshots(emitter: StdoutEmitter,
                              staking_address: str) -> bool:
    """Interactively confirm disabling of taking snapshots with user agreements."""
    emitter.message(SNAPSHOTS_DISABLING_AGREEMENT)
    click.confirm(
        CONFIRM_DISABLE_SNAPSHOTS.format(staking_address=staking_address),
        abort=True)
    return True
Exemplo n.º 6
0
def destroy_configuration(emitter: StdoutEmitter,
                          character_config: CharacterConfiguration,
                          force: bool = False) -> None:
    """Destroy a character configuration and report rhe result with an emitter."""
    if not force:
        confirm_destroy_configuration(config=character_config)
    character_config.destroy()
    emitter.message(SUCCESSFUL_DESTRUCTION, color='green')
    character_config.log.debug(SUCCESSFUL_DESTRUCTION)
Exemplo n.º 7
0
def confirm_enable_restaking(emitter: StdoutEmitter,
                             staking_address: str) -> bool:
    """Interactively confirm enabling of the restaking with user agreements."""
    emitter.message(
        RESTAKING_AGREEMENT.format(staking_address=staking_address))
    click.confirm(
        CONFIRM_ENABLE_RESTAKING.format(staking_address=staking_address),
        abort=True)
    return True
Exemplo n.º 8
0
 def _shutdown_ursula(self, halt_reactor=False):
     emitter = StdoutEmitter()
     emitter.message(
         f'x [Operator {self._ursula.operator_address} is no longer bonded to any '
         f'staking provider] - Commencing auto-shutdown sequence...',
         color="red")
     try:
         raise self.OperatorNoLongerBonded()
     finally:
         self._ursula.stop(halt_reactor=halt_reactor)
Exemplo n.º 9
0
def unlock_nucypher_keystore(emitter: StdoutEmitter, password: str, character_configuration: CharacterConfiguration) -> bool:
    """Unlocks a nucypher keystore and attaches it to the supplied configuration if successful."""
    emitter.message(DECRYPTING_CHARACTER_KEYSTORE.format(name=character_configuration.NAME.capitalize()), color='yellow')

    # precondition
    if character_configuration.dev_mode:
        return True  # Dev accounts are always unlocked

    # unlock
    character_configuration.keystore.unlock(password=password)  # Takes ~3 seconds, ~1GB Ram
    return True
Exemplo n.º 10
0
def show(query, qrcode):
    """
    Lookup and view existing character card
    QUERY can be either the card id or nickname.
    """
    emitter = StdoutEmitter()
    try:
        card = select_card(emitter=emitter, card_identifier=query)
    except Card.UnknownCard as e:
        return emitter.error(str(e))
    paint_single_card(emitter=emitter, card=card, qrcode=qrcode)
Exemplo n.º 11
0
def select_network(emitter: StdoutEmitter) -> str:
    """Interactively select a network from nucypher networks inventory list"""
    headers = ["Network"]
    rows = [[n] for n in NetworksInventory.NETWORKS]
    emitter.echo(tabulate(rows, headers=headers, showindex='always'))
    choice = click.prompt(SELECT_NETWORK,
                          default=0,
                          type=click.IntRange(
                              0,
                              len(NetworksInventory.NETWORKS) - 1))
    network = NetworksInventory.NETWORKS[choice]
    return network
Exemplo n.º 12
0
 def __cache_addresses(self) -> None:
     """
     Derives trezor ethereum addresses up to ADDRESS_CACHE_SIZE relative to
     the calculated base path and internally caches them for later use.
     """
     emitter = StdoutEmitter()
     for index in range(self.ADDRESS_CACHE_SIZE):
         hd_path = self.__get_address_path(index=index)
         address = self.__derive_account(hd_path=hd_path)
         self.__addresses[address] = hd_path
         message = f"Derived {address} ({self.derivation_root}/{index})"
         emitter.message(message)
Exemplo n.º 13
0
def select_network(emitter: StdoutEmitter,
                   message: Optional[str] = None) -> str:
    """Interactively select a network from nucypher networks inventory list"""
    emitter.message(message=message or str(), color='yellow')
    rows = [[n] for n in NetworksInventory.NETWORKS]
    emitter.echo(tabulate(rows, showindex='always'))
    choice = click.prompt(SELECT_NETWORK,
                          default=0,
                          type=click.IntRange(
                              0,
                              len(NetworksInventory.NETWORKS) - 1))
    network = NetworksInventory.NETWORKS[choice]
    return network
Exemplo n.º 14
0
def retrieve_events(emitter: StdoutEmitter,
                    agent: EthereumContractAgent,
                    event_name: str,
                    from_block: BlockIdentifier,
                    to_block: BlockIdentifier,
                    argument_filters: Dict,
                    csv_output_file: Optional[Path] = None) -> None:
    if csv_output_file:
        if csv_output_file.exists():
            click.confirm(CONFIRM_OVERWRITE_EVENTS_CSV_FILE.format(csv_file=csv_output_file), abort=True)
        available_events = write_events_to_csv_file(csv_file=csv_output_file,
                                                    agent=agent,
                                                    event_name=event_name,
                                                    from_block=from_block,
                                                    to_block=to_block,
                                                    argument_filters=argument_filters)
        if available_events:
            emitter.echo(f"{agent.contract_name}::{event_name} events written to {csv_output_file}",
                         bold=True,
                         color='green')
        else:
            emitter.echo(f'No {agent.contract_name}::{event_name} events found', color='yellow')
    else:
        event = agent.contract.events[event_name]
        emitter.echo(f"{event_name}:", bold=True, color='yellow')
        entries = event.getLogs(fromBlock=from_block, toBlock=to_block, argument_filters=argument_filters)
        for event_record in entries:
            emitter.echo(f"  - {EventRecord(event_record)}")
Exemplo n.º 15
0
def _list():
    """Show all character cards"""
    emitter = StdoutEmitter()
    if not Card.CARD_DIR.is_dir():
        Card.CARD_DIR.mkdir()
    card_filepaths = list(Card.CARD_DIR.iterdir())
    if not card_filepaths:
        emitter.error(
            f'No cards found at {Card.CARD_DIR}.  '
            f"To create one run 'nucypher {contacts.name} {create.name}'.")
    cards = list()
    for filename in card_filepaths:
        card = Card.load(filepath=Card.CARD_DIR / filename)
        cards.append(card)
    paint_cards(emitter=emitter, cards=cards, as_table=True)
Exemplo n.º 16
0
def test_ursula_command_protocol_creation(ursula):

    emitter = StdoutEmitter()
    protocol = UrsulaCommandProtocol(ursula=ursula, emitter=emitter)

    assert protocol.ursula == ursula
    assert b'Ursula' in protocol.prompt
Exemplo n.º 17
0
def confirm_deployment(
        emitter: StdoutEmitter,
        deployer_interface: BlockchainDeployerInterface) -> bool:
    """
    Interactively confirm deployment by asking the user to type the ALL CAPS name of
    the network they are deploying to or 'DEPLOY' if the network if not a known public chain.

    Aborts if the confirmation word is incorrect.
    """
    if deployer_interface.client.chain_name == UNKNOWN_DEVELOPMENT_CHAIN_ID or deployer_interface.client.is_local:
        expected_chain_name = 'DEPLOY'
    else:
        expected_chain_name = deployer_interface.client.chain_name
    if click.prompt(f"Type '{expected_chain_name.upper()}' to continue"
                    ) != expected_chain_name.upper():
        emitter.echo(ABORT_DEPLOYMENT, color='red', bold=True)
        raise click.Abort(ABORT_DEPLOYMENT)
    return True
Exemplo n.º 18
0
def unbond(registry_filepath, eth_provider_uri, signer_uri, staking_provider,
           network, force):
    """Unbonds an operator from an authorized staking provider."""

    #
    # Setup
    #

    emitter = StdoutEmitter()
    if not signer_uri:
        emitter.message('--signer is required', color='red')
        raise click.Abort()
    if not network:
        network = select_network(emitter=emitter,
                                 network_type=NetworksInventory.ETH)

    connect_to_blockchain(eth_provider_uri=eth_provider_uri, emitter=emitter)
    registry = get_registry(network=network,
                            registry_filepath=registry_filepath)
    agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry)
    signer = Signer.from_signer_uri(signer_uri)
    transacting_power = TransactingPower(account=staking_provider,
                                         signer=signer)

    #
    # Check
    #

    bonded, onchain_operator_address = is_bonded(
        agent=agent, staking_provider=staking_provider, return_address=True)
    if not bonded:
        emitter.message(NOT_BONDED.format(provider=staking_provider),
                        color='red')
        raise click.Abort()
    check_bonding_requirements(emitter=emitter,
                               agent=agent,
                               staking_provider=staking_provider)

    #
    # Unbond
    #

    if not force:
        click.confirm(CONFIRM_UNBONDING.format(
            provider=staking_provider, operator=onchain_operator_address),
                      abort=True)
    transacting_power.unlock(password=get_client_password(
        checksum_address=staking_provider,
        envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD))
    emitter.echo(UNBONDING.format(operator=onchain_operator_address))
    receipt = agent.bond_operator(operator=NULL_ADDRESS,
                                  transacting_power=transacting_power,
                                  staking_provider=staking_provider)
    paint_receipt_summary(receipt=receipt, emitter=emitter)
Exemplo n.º 19
0
def connect_to_blockchain(emitter: StdoutEmitter,
                          provider_uri: str,
                          debug: bool = False,
                          light: bool = False
                          ) -> BlockchainInterface:
    try:
        # Note: Conditional for test compatibility.
        if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
            BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri,
                                                            light=light,
                                                            emitter=emitter)
        emitter.echo(message=CONNECTING_TO_BLOCKCHAIN)
        blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
        return blockchain
    except Exception as e:
        if debug:
            raise
        emitter.echo(str(e), bold=True, color='red')
        raise click.Abort
Exemplo n.º 20
0
def collect_keys_from_card(emitter: StdoutEmitter, card_identifier: str,
                           force: bool):
    emitter.message(f"Searching contacts for {card_identifier}\n",
                    color='yellow')
    card = Card.load(identifier=card_identifier)

    if card.character is not Bob:
        emitter.error('Grantee card is not a Bob.')
        raise click.Abort
    paint_single_card(emitter=emitter, card=card)

    if not force:
        click.confirm('Is this the correct grantee (Bob)?', abort=True)

    bob_encrypting_key = bytes(card.encrypting_key).hex()
    bob_verifying_key = bytes(card.verifying_key).hex()
    public_keys = PublicKeys(encrypting_key=bob_encrypting_key,
                             verifying_key=bob_verifying_key)
    return public_keys
Exemplo n.º 21
0
def test_get_nucypher_password(mock_stdin, mock_account, confirm, capsys):
    mock_stdin.password(INSECURE_DEVELOPMENT_PASSWORD, confirm=confirm)
    result = get_nucypher_password(emitter=StdoutEmitter(), confirm=confirm)
    assert result == INSECURE_DEVELOPMENT_PASSWORD
    assert mock_stdin.empty()
    captured = capsys.readouterr()
    assert COLLECT_NUCYPHER_PASSWORD in captured.out
    if confirm:
        prompt = COLLECT_NUCYPHER_PASSWORD + f" ({Keystore._MINIMUM_PASSWORD_LENGTH} character minimum)"
        assert prompt in captured.out
Exemplo n.º 22
0
def collect_operator_ip_address(emitter: StdoutEmitter, network: str, force: bool = False) -> str:

    # From node swarm
    try:
        message = f'Detecting external IP address automatically'
        emitter.message(message, verbosity=2)
        ip = determine_external_ip_address(network=network)
    except UnknownIPAddress:
        if force:
            raise
        emitter.message('Cannot automatically determine external IP address - input required')
        ip = click.prompt(COLLECT_URSULA_IPV4_ADDRESS, type=OPERATOR_IP)

    # Confirmation
    if not force:
        if not click.confirm(CONFIRM_URSULA_IPV4_ADDRESS.format(rest_host=ip)):
            ip = click.prompt(COLLECT_URSULA_IPV4_ADDRESS, type=OPERATOR_IP)

    validate_operator_ip(ip=ip)
    return ip
Exemplo n.º 23
0
def select_network(emitter: StdoutEmitter,
                   network_type: str,
                   message: Optional[str] = None) -> str:
    """Interactively select a network from nucypher networks inventory list"""
    emitter.message(message=message or str(), color="yellow")
    if network_type == NetworksInventory.ETH:
        network_list = NetworksInventory.ETH_NETWORKS
    elif network_type == NetworksInventory.POLYGON:
        network_list = NetworksInventory.POLY_NETWORKS
    else:
        raise (ValueError("Network type must be either 'eth' or 'polygon'"))
    rows = [[n] for n in network_list]
    emitter.echo(tabulate(rows, showindex="always"))
    choice = click.prompt(
        SELECT_NETWORK,
        default=0,
        type=click.IntRange(0,
                            len(rows) - 1),
    )
    network = network_list[choice]
    return network
Exemplo n.º 24
0
def deployer_pre_launch_warnings(emitter: StdoutEmitter, etherscan: bool, hw_wallet: bool) -> None:
    if not hw_wallet:
        emitter.echo(NO_HARDWARE_WALLET_WARNING, color='yellow')
    if etherscan:
        emitter.echo(ETHERSCAN_FLAG_ENABLED_WARNING, color='yellow')
    else:
        emitter.echo(ETHERSCAN_FLAG_DISABLED_WARNING, color='yellow')
Exemplo n.º 25
0
def get_or_update_configuration(emitter: StdoutEmitter,
                                filepath: Path,
                                config_class: Type[CharacterConfiguration],
                                updates: Optional[dict] = None) -> None:
    """
    Utility for writing updates to an existing configuration file then displaying the result.
    If the config file is invalid, try very hard to display the problem.  If there are no updates,
    the config file will be displayed without changes.
    """
    try:
        config = config_class.from_configuration_file(filepath=filepath)
    except FileNotFoundError:
        return handle_missing_configuration_file(
            character_config_class=config_class, config_file=filepath)
    except config_class.ConfigurationError:
        return handle_invalid_configuration_file(emitter=emitter,
                                                 config_class=config_class,
                                                 filepath=filepath)

    emitter.echo(
        f"{config_class.NAME.capitalize()} Configuration {filepath} \n {'='*55}"
    )
    if updates:
        pretty_fields = ', '.join(updates)
        emitter.message(SUCCESSFUL_UPDATE_CONFIGURATION_VALUES.format(
            fields=pretty_fields),
                        color='yellow')
        config.update(**updates)
    emitter.echo(config.serialize())
Exemplo n.º 26
0
def paint_all_stakes(emitter: StdoutEmitter,
                     stakeholder: 'StakeHolder',
                     paint_unlocked: bool = False) -> None:

    stakers = stakeholder.get_stakers()
    if not stakers:
        emitter.echo("No staking accounts found.")

    total_stakers = 0
    for staker in stakers:
        if not staker.stakes:
            # This staker has no active stakes.
            # TODO: Something with non-staking accounts?
            continue

        paint_stakes(emitter=emitter,
                     staker=staker,
                     paint_unlocked=paint_unlocked,
                     stakeholder=stakeholder)
        total_stakers += 1

    if not total_stakers:
        emitter.echo("No Stakes found", color='red')
Exemplo n.º 27
0
 def import_secure(cls,
                   key_material: bytes,
                   password: str,
                   keystore_dir: Optional[Path] = None) -> 'Keystore':
     """
     Generate a Keystore using a a custom pre-secured entropy blob.
     This method of keystore creation does not generate a mnemonic phrase - it is assumed
     that the provided blob is recoverable and secure.
     """
     emitter = StdoutEmitter()
     emitter.message(
         f'WARNING: Key importing assumes that you have already secured your secret '
         f'and can recover it. No mnemonic will be generated.\n',
         color='yellow')
     if len(key_material) != SecretKey.serialized_size():
         raise ValueError(
             f'Entropy bytes bust be exactly {SecretKey.serialized_size()}.'
         )
     path = Keystore.__save(secret=key_material,
                            password=password,
                            keystore_dir=keystore_dir)
     keystore = cls(keystore_path=path)
     return keystore
Exemplo n.º 28
0
def validate_grant_command(emitter: StdoutEmitter, alice: Alice, force: bool,
                           bob: str, bob_verifying_key: str,
                           bob_encrypting_key: str, label: str,
                           expiration: maya.MayaDT, rate: int, value: int):

    # Force mode validation
    if force:
        required = (Precondition(
            options='--bob or --bob-encrypting-key and --bob-verifying-key.',
            condition=bob or all((bob_verifying_key, bob_encrypting_key))),
                    Precondition(options='--label', condition=bool(label)),
                    Precondition(options='--expiration',
                                 condition=bool(expiration)))
        triggered = False
        for condition in required:
            # see what condition my condition was in.
            if not condition.condition:
                triggered = True
                emitter.error(
                    f'Missing options in force mode: {condition.options}',
                    color="red")
        if triggered:
            raise click.Abort()

    # Handle federated
    if alice.federated_only:
        if any((value, rate)):
            message = "Can't use --value or --rate with a federated Alice."
            raise click.BadOptionUsage(option_name="--value, --rate",
                                       message=click.style(message, fg="red"))
    elif bool(value) and bool(rate):
        raise click.BadOptionUsage(option_name="--rate",
                                   message=click.style(
                                       "Can't use --value if using --rate",
                                       fg="red"))

    # From Bob card
    if bob:
        if any((bob_encrypting_key, bob_verifying_key)):
            message = '--bob cannot be used with --bob-encrypting-key or --bob-verifying key'
            raise click.BadOptionUsage(option_name='--bob',
                                       message=click.style(message, fg="red"))

    # From hex public keys
    else:
        if not all((bob_encrypting_key, bob_verifying_key)):
            if force:
                emitter.message(
                    'Missing options in force mode: --bob or --bob-encrypting-key and --bob-verifying-key.',
                    color="red")
                click.Abort()
            emitter.message("*Caution: Only enter public keys*")
Exemplo n.º 29
0
def create(character_flag, verifying_key, encrypting_key, nickname, force):
    """Store a new character card"""
    emitter = StdoutEmitter()

    # Validate
    if not all((character_flag, verifying_key, encrypting_key)) and force:
        emitter.error(
            f'--verifying-key, --encrypting-key, and --type are required with --force enabled.'
        )

    # Card type
    from constant_sorrow.constants import ALICE, BOB
    flags = {'a': ALICE, 'b': BOB}
    if not character_flag:
        choice = click.prompt('Enter Card Type - (A)lice or (B)ob',
                              type=click.Choice(['a', 'b'],
                                                case_sensitive=False))
        character_flag = flags[choice]
    else:
        character_flag = flags[character_flag]

    # Verifying Key
    if not verifying_key:
        verifying_key = click.prompt('Enter Verifying Key',
                                     type=UMBRAL_PUBLIC_KEY_HEX)
    verifying_key = bytes.fromhex(verifying_key)  # TODO: Move / Validate

    # Encrypting Key
    if character_flag is BOB:
        if not encrypting_key:
            encrypting_key = click.prompt('Enter Encrypting Key',
                                          type=UMBRAL_PUBLIC_KEY_HEX)
        encrypting_key = bytes.fromhex(encrypting_key)  # TODO: Move / Validate

    # Init
    new_card = Card(character_flag=character_flag,
                    verifying_key=verifying_key,
                    encrypting_key=encrypting_key,
                    nickname=nickname)

    # Nickname
    if not force and not nickname:
        card_id_hex = new_card.id.hex()
        nickname = click.prompt('Enter nickname for card', default=card_id_hex)
        if nickname != card_id_hex:  # not the default
            nickname = nickname.strip()
            new_card.nickname = nickname

    # Save
    new_card.save()
    emitter.message(f'Saved new card {new_card}', color='green')
    paint_single_card(emitter=emitter, card=new_card)
Exemplo n.º 30
0
    def _confirm_generate(__words: str) -> None:
        """
        Inform the caller of new keystore seed words generation the console
        and optionally perform interactive confirmation.
        """

        # notification
        emitter = StdoutEmitter()
        emitter.message(
            f'Backup your seed words, you will not be able to view them again.\n'
        )
        emitter.message(f'{__words}\n', color='cyan')
        if not click.confirm("Have you backed up your seed phrase?"):
            emitter.message('Keystore generation aborted.', color='red')
            raise click.Abort()
        click.clear()

        # confirmation
        __response = click.prompt("Confirm seed words")
        if __response != __words:
            raise ValueError(
                'Incorrect seed word confirmation. No keystore has been created, try again.'
            )
        click.clear()