Esempio n. 1
0
def run(general_config, character_options, config_file, controller_port,
        dry_run):
    """Start Alice's web controller."""

    # Setup
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file,
                                               general_config.json_ipc)

    try:
        # RPC
        if general_config.json_ipc:
            rpc_controller = ALICE.make_rpc_controller()
            _transport = rpc_controller.make_control_transport()
            rpc_controller.start()
            return

        # HTTP
        else:
            emitter.message(f"Alice Verifying Key {bytes(ALICE.stamp).hex()}",
                            color="green",
                            bold=True)
            controller = ALICE.make_web_controller(
                crash_on_error=general_config.debug)
            ALICE.log.info('Starting HTTP Character Web Controller')
            emitter.message(
                f'Running HTTP Alice Controller at http://localhost:{controller_port}'
            )
            return controller.start(http_port=controller_port, dry_run=dry_run)

    # Handle Crash
    except Exception as e:
        ALICE.log.critical(str(e))
        emitter.message(f"{e.__class__.__name__} {e}", color='red', bold=True)
        if general_config.debug:
            raise  # Crash :-(
Esempio n. 2
0
def retrieve(general_config, character_options, config_file, label,
             policy_encrypting_key, alice_verifying_key, message_kit, ipfs,
             alice, force):
    """Obtain plaintext from encrypted data, if access was granted."""

    # Setup
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter, config_file)

    if not message_kit:
        if ipfs:
            prompt = "Enter IPFS CID for encrypted data"
        else:
            prompt = "Enter encrypted data (base64)"
        message_kit = click.prompt(prompt, type=click.STRING)

    if ipfs:
        import ipfshttpclient
        # TODO: #2108
        emitter.message(f"Connecting to IPFS Gateway {ipfs}")
        ipfs_client = ipfshttpclient.connect(ipfs)
        cid = message_kit  # Understand the message kit value as an IPFS hash.
        raw_message_kit = ipfs_client.cat(
            cid)  # cat the contents at the hash reference
        emitter.message(f"Downloaded message kit from IPFS (CID {cid})",
                        color='green')
        message_kit = raw_message_kit.decode()  # cast to utf-8

    if not alice_verifying_key:
        if alice:  # from storage
            card = Card.load(identifier=alice)
            if card.character is not Alice:
                emitter.error('Grantee card is not an Alice.')
                raise click.Abort
            alice_verifying_key = card.verifying_key.hex()
            emitter.message(
                f'{card.nickname or ("Alice #"+card.id.hex())}\n'
                f'Verifying Key  | {card.verifying_key.hex()}',
                color='green')
            if not force:
                click.confirm('Is this the correct Granter (Alice)?',
                              abort=True)
        else:  # interactive
            alice_verifying_key = click.prompt("Enter Alice's verifying key",
                                               click.STRING)

    if not force:
        if not policy_encrypting_key:
            policy_encrypting_key = click.prompt("Enter policy public key",
                                                 type=click.STRING)

        if not label:
            label = click.prompt("Enter label to retrieve", type=click.STRING)

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

    # TODO: Uncomment for Demo. Fix Cleartext Deserialization.
    # for cleartext in response['cleartexts']:
    #     print(b64decode(cleartext))

    return response
Esempio n. 3
0
def grant(general_config,
          bob,
          bob_encrypting_key,
          bob_verifying_key,
          label,
          value,
          rate,
          expiration,
          m, n,
          character_options,
          config_file,
          force):
    """Create and enact an access policy for some Bob. """

    # Setup
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)

    # Policy option validation
    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=message)
    elif bool(value) and bool(rate):
        raise click.BadOptionUsage(option_name="--rate", message="Can't use --value if using --rate")

    # Grantee selection
    if bob and any((bob_encrypting_key, bob_verifying_key)):
        message = '--bob cannot be used with --bob-encrypting-key or --bob-veryfying key'
        raise click.BadOptionUsage(option_name='--bob', message=message)

    if bob:
        card = Card.load(identifier=bob)
        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 = card.encrypting_key.hex()
        bob_verifying_key = card.verifying_key.hex()

    # Interactive collection follows:
    # TODO: Extricate to support modules
    # - Disclaimer
    # - Label
    # - Expiration Date & Time
    # - M of N
    # - Policy Value (ETH)

    # Policy Expiration
    # TODO: Remove this line when the time is right.
    paint_probationary_period_disclaimer(emitter)

    # Label
    if not label:
        label = click.prompt(f'Enter label to grant Bob {bob_verifying_key[:8]}', type=click.STRING)

    if not force and not expiration:
        if ALICE.duration_periods:
            # TODO: use a default in days or periods?
            expiration = maya.now() + timedelta(days=ALICE.duration_periods)  # default
            if not click.confirm(f'Use default policy duration (expires {expiration})?'):
                expiration = click.prompt('Enter policy expiration datetime', type=click.DateTime())
        else:
            # No policy duration default default available; Go interactive
            expiration = click.prompt('Enter policy expiration datetime', type=click.DateTime())

    # TODO: Remove this line when the time is right.
    enforce_probationary_period(emitter=emitter, expiration=expiration)

    # Policy Threshold and Shares
    if not n:
        n = ALICE.n
        if not force and not click.confirm(f'Use default value for N ({n})?', default=True):
            n = click.prompt('Enter total number of shares (N)', type=click.INT)
    if not m:
        m = ALICE.m
        if not force and not click.confirm(f'Use default value for M ({m})?', default=True):
            m = click.prompt('Enter threshold (M)', type=click.IntRange(1, n))

    # Policy Value
    policy_value_provided = bool(value) or bool(rate)
    if not ALICE.federated_only and not policy_value_provided:
        rate = ALICE.default_rate  # TODO #1709 - Fine tuning and selection of default rates
        if not force:
            default_gwei = Web3.fromWei(rate, 'gwei')
            prompt = "Confirm rate of {node_rate} gwei ({total_rate} gwei per period)?"
            if not click.confirm(prompt.format(node_rate=default_gwei, total_rate=default_gwei*n), default=True):
                interactive_rate = click.prompt('Enter rate per period in gwei', type=GWEI)
                # TODO: Validate interactively collected rate (#1709)
                click.confirm(prompt.format(node_rate=rate, total_rate=rate*n), default=True, abort=True)
                rate = Web3.toWei(interactive_rate, 'gwei')

    # Request
    grant_request = {
        'bob_encrypting_key': bob_encrypting_key,
        'bob_verifying_key': bob_verifying_key,
        'label': label,
        'm': m,
        'n': n,
        'expiration': expiration,
    }
    if not ALICE.federated_only:
        if value:
            grant_request['value'] = value
        elif rate:
            grant_request['rate'] = rate  # in wei

    if not force and not general_config.json_ipc:
        confirm_staged_grant(emitter=emitter, grant_request=grant_request)
    emitter.echo(f'Granting Access to {bob_verifying_key[:8]}', color='yellow')
    return ALICE.controller.grant(request=grant_request)
Esempio n. 4
0
def prolong(general_config, transacting_staker_options, config_file, force,
            lock_periods, index):
    """Prolong an existing stake's duration."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    action_period = STAKEHOLDER.staking_agent.get_current_period()
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    # Handle account selection
    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Handle stake update and selection
    if transacting_staker_options.staker_options.staking_address and index is not None:  # 0 is valid.
        STAKEHOLDER.stakes = StakeList(
            registry=STAKEHOLDER.registry,
            checksum_address=transacting_staker_options.staker_options.
            staking_address)
        STAKEHOLDER.stakes.refresh()
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(stakeholder=STAKEHOLDER, emitter=emitter)

    #
    # Prolong
    #

    # Interactive
    if not lock_periods:
        max_extension = MAX_UINT16 - current_stake.final_locked_period
        # +1 because current period excluded
        min_extension = economics.minimum_locked_periods - current_stake.periods_remaining + 1
        if min_extension < 1:
            min_extension = 1
        duration_extension_range = click.IntRange(min=min_extension,
                                                  max=max_extension,
                                                  clamp=False)
        lock_periods = click.prompt(PROMPT_PROLONG_VALUE.format(
            minimum=min_extension, maximum=max_extension),
                                    type=duration_extension_range)
    if not force:
        click.confirm(CONFIRM_PROLONG.format(lock_periods=lock_periods),
                      abort=True)
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    # Non-interactive: Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if action_period != last_second_current_period:
        emitter.echo(PERIOD_ADVANCED_WARNING, red='red')
        raise click.Abort

    # Authenticate and Execute
    STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address,
                           password=password)
    receipt = STAKEHOLDER.prolong_stake(stake_index=current_stake.index,
                                        additional_periods=lock_periods)

    # Report
    emitter.echo(SUCCESSFUL_STAKE_PROLONG, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter,
                          receipt=receipt,
                          chain_name=blockchain.client.chain_name)
    paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
Esempio n. 5
0
def create(general_config, transacting_staker_options, config_file, force,
           value, lock_periods):
    """Initialize a new stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Dynamic click types (Economics)
    min_locked = economics.minimum_allowed_locked
    stake_value_range = click.FloatRange(
        min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
    stake_duration_range = click.IntRange(min=economics.minimum_locked_periods,
                                          clamp=False)

    #
    # Stage Stake
    #

    if not value:
        token_balance = NU.from_nunits(
            STAKEHOLDER.token_agent.get_balance(staking_address))
        lower_limit = NU.from_nunits(
            STAKEHOLDER.economics.minimum_allowed_locked)
        upper_limit = min(
            token_balance,
            NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked))
        value = click.prompt(
            f"Enter stake value in NU "
            f"({lower_limit} - {upper_limit})",
            type=stake_value_range,
            default=upper_limit.to_tokens())
    value = NU.from_tokens(value)

    if not lock_periods:
        min_locktime = STAKEHOLDER.economics.minimum_locked_periods
        default_locktime = STAKEHOLDER.economics.maximum_rewarded_periods
        max_locktime = MAX_UINT16 - STAKEHOLDER.staking_agent.get_current_period(
        )
        prompt = f"Enter stake duration ({min_locktime} - {max_locktime})"
        lock_periods = click.prompt(prompt,
                                    type=stake_duration_range,
                                    default=default_locktime)

    start_period = STAKEHOLDER.staking_agent.get_current_period() + 1
    unlock_period = start_period + lock_periods

    #
    # Review and Publish
    #

    if not force:
        confirm_large_stake(value=value, lock_periods=lock_periods)
        paint_staged_stake(emitter=emitter,
                           stakeholder=STAKEHOLDER,
                           staking_address=staking_address,
                           stake_value=value,
                           lock_periods=lock_periods,
                           start_period=start_period,
                           unlock_period=unlock_period)

        confirm_staged_stake(staker_address=staking_address,
                             value=value,
                             lock_periods=lock_periods)

    # Last chance to bail
    click.confirm(CONFIRM_BROADCAST_CREATE_STAKE, abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    # Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if start_period != last_second_current_period + 1:
        emitter.echo(PERIOD_ADVANCED_WARNING, color='red')
        raise click.Abort

    # Authenticate and Execute
    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)

    new_stake = STAKEHOLDER.initialize_stake(amount=value,
                                             lock_periods=lock_periods)
    paint_staking_confirmation(emitter=emitter,
                               staker=STAKEHOLDER,
                               new_stake=new_stake)
Esempio n. 6
0
def list_stakes(general_config, staker_options, config_file, all):
    """List active stakes for current stakeholder."""
    emitter = setup_emitter(general_config)
    STAKEHOLDER = staker_options.create_character(emitter, config_file)
    paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER, paint_inactive=all)
Esempio n. 7
0
def destroy(general_config, config_options, config_file, force):
    """Delete existing Alice's configuration."""
    emitter = setup_emitter(general_config)
    alice_config = config_options.create_config(emitter, config_file)
    destroy_configuration(emitter, character_config=alice_config, force=force)
Esempio n. 8
0
def revoke(general_config, bob_verifying_key, label, character_options, config_file):
    """Revoke a policy."""
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)
    revoke_request = {'label': label, 'bob_verifying_key': bob_verifying_key}
    return ALICE.controller.revoke(request=revoke_request)
Esempio n. 9
0
def destroy(general_config, config_options, config_file, force):
    """Destroy Felix Configuration."""
    emitter = setup_emitter(general_config, config_options.checksum_address)
    felix_config = config_options.create_config(emitter, config_file)
    destroy_configuration(emitter, character_config=felix_config, force=force)
Esempio n. 10
0
def increase(general_config, transacting_staker_options, config_file, force, value, index, only_lock):
    """Increase an existing stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Handle stake update and selection
    if index is not None:  # 0 is valid.
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(staker=STAKEHOLDER, emitter=emitter)

    #
    # Stage Stake
    #

    if not value:
        if not only_lock:
            only_lock = not click.prompt(PROMPT_DEPOSIT_OR_LOCK, type=click.BOOL, default=True)

        token_balance = STAKEHOLDER.token_balance if not only_lock else STAKEHOLDER.calculate_staking_reward()
        locked_tokens = STAKEHOLDER.locked_tokens(periods=1).to_nunits()
        upper_limit = min(token_balance, NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked - locked_tokens))

        if token_balance == 0:
            emitter.echo(INSUFFICIENT_BALANCE_TO_INCREASE, color='red')
            raise click.Abort
        if upper_limit == 0:
            emitter.echo(MAXIMUM_STAKE_REACHED, color='red')
            raise click.Abort

        stake_value_range = click.FloatRange(min=0, max=upper_limit.to_tokens(), clamp=False)
        value = click.prompt(PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit),
                             type=stake_value_range)
    value = NU.from_tokens(value)

    #
    # Review and Publish
    #

    if not force:
        lock_periods = current_stake.periods_remaining - 1
        current_period = STAKEHOLDER.staking_agent.get_current_period()
        unlock_period = current_stake.final_locked_period + 1

        confirm_large_stake(value=value, lock_periods=lock_periods)
        paint_staged_stake(emitter=emitter,
                           stakeholder=STAKEHOLDER,
                           staking_address=staking_address,
                           stake_value=value,
                           lock_periods=lock_periods,
                           start_period=current_period + 1,
                           unlock_period=unlock_period)
        click.confirm(CONFIRM_INCREASING_STAKE.format(stake_index=current_stake.index, value=value), abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # Execute
    receipt = STAKEHOLDER.increase_stake(stake=current_stake, amount=value, only_lock=only_lock)

    # Report
    emitter.echo(SUCCESSFUL_STAKE_INCREASE, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name)
    paint_stakes(emitter=emitter, staker=STAKEHOLDER)
Esempio n. 11
0
def create(general_config, transacting_staker_options, config_file, force, value, lock_periods, only_lock):
    """Initialize a new stake."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Dynamic click types (Economics)
    min_locked = economics.minimum_allowed_locked
    stake_value_range = click.FloatRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
    stake_duration_range = click.IntRange(min=economics.minimum_locked_periods, clamp=False)

    #
    # Stage Stake
    #

    if not value:
        if not only_lock:
            only_lock = not click.prompt(PROMPT_DEPOSIT_OR_LOCK, type=click.BOOL, default=True)

        token_balance = STAKEHOLDER.token_balance if not only_lock else STAKEHOLDER.calculate_staking_reward()
        lower_limit = NU.from_nunits(STAKEHOLDER.economics.minimum_allowed_locked)
        locked_tokens = STAKEHOLDER.locked_tokens(periods=1).to_nunits()
        upper_limit = min(token_balance, NU.from_nunits(STAKEHOLDER.economics.maximum_allowed_locked - locked_tokens))

        if token_balance <= lower_limit:
            emitter.echo(INSUFFICIENT_BALANCE_TO_CREATE, color='red')
            raise click.Abort
        if upper_limit <= lower_limit:
            emitter.echo(MAXIMUM_STAKE_REACHED, color='red')
            raise click.Abort

        value = click.prompt(PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_limit=upper_limit),
                             type=stake_value_range,
                             default=upper_limit.to_tokens())
    value = NU.from_tokens(value)

    if not lock_periods:
        min_locktime = STAKEHOLDER.economics.minimum_locked_periods
        default_locktime = STAKEHOLDER.economics.maximum_rewarded_periods
        max_locktime = MAX_UINT16 - STAKEHOLDER.staking_agent.get_current_period()
        lock_periods = click.prompt(PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime,
                                                                            max_locktime=max_locktime),
                                    type=stake_duration_range,
                                    default=default_locktime)

    start_period = STAKEHOLDER.staking_agent.get_current_period() + 1
    unlock_period = start_period + lock_periods

    #
    # Review and Publish
    #

    if not force:
        confirm_large_stake(value=value, lock_periods=lock_periods)
        paint_staged_stake(emitter=emitter,
                           stakeholder=STAKEHOLDER,
                           staking_address=staking_address,
                           stake_value=value,
                           lock_periods=lock_periods,
                           start_period=start_period,
                           unlock_period=unlock_period)

        confirm_staged_stake(staker_address=staking_address, value=value, lock_periods=lock_periods)

    # Last chance to bail
    click.confirm(CONFIRM_BROADCAST_CREATE_STAKE, abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain, client_account)
    STAKEHOLDER.assimilate(password=password)

    # Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if start_period != last_second_current_period + 1:
        emitter.echo(PERIOD_ADVANCED_WARNING, color='red')
        raise click.Abort

    # Execute
    receipt = STAKEHOLDER.initialize_stake(amount=value, lock_periods=lock_periods, only_lock=only_lock)
    paint_staking_confirmation(emitter=emitter, staker=STAKEHOLDER, receipt=receipt)
Esempio n. 12
0
def accounts(general_config, staker_options, config_file):
    """Show ETH and NU balances for stakeholder's accounts."""
    emitter = setup_emitter(general_config)
    STAKEHOLDER = staker_options.create_character(emitter, config_file)
    paint_staking_accounts(emitter=emitter, wallet=STAKEHOLDER.wallet, registry=STAKEHOLDER.registry)
Esempio n. 13
0
def init_stakeholder(general_config, config_root, force, config_options):
    """Create a new stakeholder configuration."""
    emitter = setup_emitter(general_config)
    new_stakeholder = config_options.generate_config(config_root)
    filepath = new_stakeholder.to_configuration_file(override=force)
    emitter.echo(SUCCESSFUL_NEW_STAKEHOLDER_CONFIG.format(filepath=filepath), color='green')
Esempio n. 14
0
def run(general_config, network, provider_uri, federated_only, teacher_uri,
        registry_filepath, min_stake, http_port, tls_certificate_filepath,
        tls_key_filepath, basic_auth_filepath, allow_origins, dry_run, eager):
    """Start Porter's Web controller."""
    emitter = setup_emitter(general_config, banner=Porter.BANNER)

    # HTTP/HTTPS
    if bool(tls_key_filepath) ^ bool(tls_certificate_filepath):
        raise click.BadOptionUsage(
            option_name='--tls-key-filepath, --tls-certificate-filepath',
            message=PORTER_BOTH_TLS_KEY_AND_CERTIFICATION_MUST_BE_PROVIDED)

    is_https = (tls_key_filepath and tls_certificate_filepath)

    # check authentication
    if basic_auth_filepath and not is_https:
        raise click.BadOptionUsage(option_name='--basic-auth-filepath',
                                   message=PORTER_BASIC_AUTH_REQUIRES_HTTPS)

    if federated_only:
        if not teacher_uri:
            raise click.BadOptionUsage(
                option_name='--teacher',
                message="--teacher is required for federated porter.")

        teacher = Ursula.from_teacher_uri(
            teacher_uri=teacher_uri, federated_only=True,
            min_stake=min_stake)  # min stake is irrelevant for federated
        PORTER = Porter(domain=TEMPORARY_DOMAIN,
                        start_learning_now=eager,
                        known_nodes={teacher},
                        verify_node_bonding=False,
                        federated_only=True)
    else:
        # decentralized/blockchain
        if not provider_uri:
            raise click.BadOptionUsage(
                option_name='--provider',
                message="--provider is required for decentralized porter.")
        if not network:
            # should never happen - network defaults to 'mainnet' if not specified
            raise click.BadOptionUsage(
                option_name='--network',
                message="--network is required for decentralized porter.")

        registry = get_registry(network=network,
                                registry_filepath=registry_filepath)
        teacher = None
        if teacher_uri:
            teacher = Ursula.from_teacher_uri(
                teacher_uri=teacher_uri,
                federated_only=False,  # always False
                min_stake=min_stake,
                registry=registry)

        PORTER = Porter(domain=network,
                        known_nodes={teacher} if teacher else None,
                        registry=registry,
                        start_learning_now=eager,
                        provider_uri=provider_uri)

    # RPC
    if general_config.json_ipc:
        rpc_controller = PORTER.make_rpc_controller()
        _transport = rpc_controller.make_control_transport()
        rpc_controller.start()
        return

    emitter.message(f"Network: {PORTER.domain.capitalize()}", color='green')
    if not federated_only:
        emitter.message(f"Provider: {provider_uri}", color='green')

    # firm up falsy status (i.e. change specified empty string to None)
    allow_origins = allow_origins if allow_origins else None
    # covert to list of strings/regexes
    allow_origins_list = None
    if allow_origins:
        allow_origins_list = allow_origins.split(
            ",")  # split into list of origins to allow
        emitter.message(PORTER_CORS_ALLOWED_ORIGINS.format(
            allow_origins=allow_origins_list),
                        color='green')

    if basic_auth_filepath:
        emitter.message(PORTER_BASIC_AUTH_ENABLED, color='green')

    controller = PORTER.make_web_controller(
        crash_on_error=False,
        htpasswd_filepath=basic_auth_filepath,
        cors_allow_origins_list=allow_origins_list)
    http_scheme = "https" if is_https else "http"
    message = PORTER_RUN_MESSAGE.format(http_scheme=http_scheme,
                                        http_port=http_port)
    emitter.message(message, color='green', bold=True)
    return controller.start(port=http_port,
                            tls_key_filepath=tls_key_filepath,
                            tls_certificate_filepath=tls_certificate_filepath,
                            dry_run=dry_run)
Esempio n. 15
0
def public_keys(general_config, character_options, config_file):
    """Obtain Alice's public verification and encryption keys."""
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
    response = ALICE.controller.public_keys()
    return response
Esempio n. 16
0
def derive_policy_pubkey(general_config, label, character_options, config_file):
    """Get a policy public key from a policy label."""
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
    return ALICE.controller.derive_policy_encrypting_key(label=label)
Esempio n. 17
0
def restake(general_config, transacting_staker_options, config_file, enable,
            lock_until, force):
    """Manage re-staking with --enable or --disable."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Inner Exclusive Switch
    if lock_until:
        if not force:
            confirm_enable_restaking_lock(emitter,
                                          staking_address=staking_address,
                                          release_period=lock_until)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        receipt = STAKEHOLDER.enable_restaking_lock(release_period=lock_until)
        emitter.echo(SUCCESSFUL_ENABLE_RESTAKE_LOCK.format(
            staking_address=staking_address, lock_until=lock_until),
                     color='green',
                     verbosity=1)
    elif enable:
        if not force:
            confirm_enable_restaking(emitter, staking_address=staking_address)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        receipt = STAKEHOLDER.enable_restaking()
        emitter.echo(SUCCESSFUL_ENABLE_RESTAKING.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)
    else:
        if not force:
            click.confirm(CONFIRM_DISABLE_RESTAKING.format(
                staking_address=staking_address),
                          abort=True)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        receipt = STAKEHOLDER.disable_restaking()
        emitter.echo(SUCCESSFUL_DISABLE_RESTAKING.format(
            staking_address=staking_address),
                     color='green',
                     verbosity=1)

    paint_receipt_summary(receipt=receipt,
                          emitter=emitter,
                          chain_name=blockchain.client.chain_name)
Esempio n. 18
0
def forget(general_config, config_options, config_file):
    """Forget all known nodes."""
    emitter = setup_emitter(general_config, config_options.worker_address)
    _pre_launch_warnings(emitter, dev=config_options.dev, force=None)
    ursula_config = config_options.create_config(emitter, config_file)
    forget_nodes(emitter, configuration=ursula_config)
Esempio n. 19
0
def collect_reward(general_config, transacting_staker_options, config_file,
                   staking_reward, policy_fee, withdraw_address, force):
    """Withdraw staking reward."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    if not staking_reward and not policy_fee:
        raise click.BadArgumentUsage(
            f"Either --staking-reward or --policy-fee must be True to collect rewards."
        )

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    password = None

    if staking_reward:
        # Note: Sending staking / inflation rewards to another account is not allowed.
        reward_amount = NU.from_nunits(STAKEHOLDER.calculate_staking_reward())
        if reward_amount == 0:
            emitter.echo(NO_TOKENS_TO_WITHDRAW, color='red')
            raise click.Abort

        emitter.echo(message=COLLECTING_TOKEN_REWARD.format(
            reward_amount=reward_amount))

        withdrawing_last_portion = STAKEHOLDER.non_withdrawable_stake() == 0
        if not force and withdrawing_last_portion and STAKEHOLDER.mintable_periods(
        ) > 0:
            click.confirm(CONFIRM_COLLECTING_WITHOUT_MINTING, abort=True)

        # Authenticate and Execute
        password = transacting_staker_options.get_password(
            blockchain, client_account)
        STAKEHOLDER.assimilate(password=password)

        staking_receipt = STAKEHOLDER.collect_staking_reward()
        paint_receipt_summary(
            receipt=staking_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)

    if policy_fee:
        fee_amount = Web3.fromWei(STAKEHOLDER.calculate_policy_fee(), 'ether')
        if fee_amount == 0:
            emitter.echo(NO_FEE_TO_WITHDRAW, color='red')
            raise click.Abort

        emitter.echo(message=COLLECTING_ETH_FEE.format(fee_amount=fee_amount))

        if password is None:
            # Authenticate and Execute
            password = transacting_staker_options.get_password(
                blockchain, client_account)
            STAKEHOLDER.assimilate(password=password)

        policy_receipt = STAKEHOLDER.collect_policy_fee(
            collector_address=withdraw_address)
        paint_receipt_summary(
            receipt=policy_receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)
Esempio n. 20
0
def grant(general_config, bob, bob_encrypting_key, bob_verifying_key, label,
          value, rate, expiration, threshold, shares, character_options,
          config_file, force):
    """Create and enact an access policy for Bob."""

    # Setup
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(
        emitter=emitter,
        config_file=config_file,
        json_ipc=general_config.json_ipc)
    validate_grant_command(emitter=emitter,
                           alice=ALICE,
                           force=force,
                           bob=bob,
                           label=label,
                           rate=rate,
                           value=value,
                           expiration=expiration,
                           bob_encrypting_key=bob_encrypting_key,
                           bob_verifying_key=bob_verifying_key)

    # Collect
    bob_public_keys = collect_bob_public_keys(
        emitter=emitter,
        force=force,
        card_identifier=bob,
        bob_encrypting_key=bob_encrypting_key,
        bob_verifying_key=bob_verifying_key)

    policy = collect_policy_parameters(
        emitter=emitter,
        alice=ALICE,
        force=force,
        bob_identifier=bob_public_keys.verifying_key[:8],
        label=label,
        threshold=threshold,
        shares=shares,
        rate=rate,
        value=value,
        expiration=expiration)

    grant_request = {
        'bob_encrypting_key': bob_public_keys.encrypting_key,
        'bob_verifying_key': bob_public_keys.verifying_key,
        'label': policy.label,
        'threshold': policy.threshold,
        'shares': policy.shares,
        'expiration': policy.expiration,
    }
    if not ALICE.federated_only:
        # These values can be 0
        if policy.value is not None:
            grant_request['value'] = policy.value
        elif policy.rate is not None:
            grant_request['rate'] = policy.rate  # in wei

    # Grant
    if not force and not general_config.json_ipc:
        confirm_staged_grant(
            emitter=emitter,
            grant_request=grant_request,
            federated=ALICE.federated_only,
            seconds_per_period=(None if ALICE.federated_only else
                                ALICE.economics.seconds_per_period))
    emitter.echo(f'Granting Access to {bob_public_keys.verifying_key[:8]}',
                 color='yellow')
    return ALICE.controller.grant(request=grant_request)
Esempio n. 21
0
def recover(general_config, config_options):
    # TODO: Combine with work in PR #2682
    # TODO: Integrate regeneration of configuration files
    emitter = setup_emitter(general_config, config_options.worker_address)
    recover_keystore(emitter=emitter)
Esempio n. 22
0
def bond_worker(general_config, transacting_staker_options, config_file, force,
                worker_address):
    """Bond a worker to a staker."""

    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    if not worker_address:
        worker_address = click.prompt(PROMPT_WORKER_ADDRESS,
                                      type=EIP55_CHECKSUM_ADDRESS)

    if (worker_address == staking_address) and not force:
        click.confirm(CONFIRM_WORKER_AND_STAKER_ADDRESSES_ARE_EQUAL.format(
            address=worker_address),
                      abort=True)

    # TODO: Check preconditions (e.g., minWorkerPeriods, already in use, etc)

    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    # TODO: Double-check dates
    # Calculate release datetime
    current_period = STAKEHOLDER.staking_agent.get_current_period()
    bonded_date = datetime_at_period(
        period=current_period, seconds_per_period=economics.seconds_per_period)
    min_worker_periods = STAKEHOLDER.economics.minimum_worker_periods

    release_period = current_period + min_worker_periods
    release_date = datetime_at_period(
        period=release_period,
        seconds_per_period=economics.seconds_per_period,
        start_of_period=True)

    if not force:
        click.confirm(
            f"Commit to bonding "
            f"worker {worker_address} to staker {staking_address} "
            f"for a minimum of {STAKEHOLDER.economics.minimum_worker_periods} periods?",
            abort=True)

    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
    receipt = STAKEHOLDER.bond_worker(worker_address=worker_address)

    # Report Success
    message = SUCCESSFUL_WORKER_BONDING.format(worker_address=worker_address,
                                               staking_address=staking_address)
    emitter.echo(message, color='green')
    paint_receipt_summary(emitter=emitter,
                          receipt=receipt,
                          chain_name=blockchain.client.chain_name,
                          transaction_type='bond_worker')
    emitter.echo(BONDING_DETAILS.format(current_period=current_period,
                                        bonded_date=bonded_date),
                 color='green')
    emitter.echo(BONDING_RELEASE_INFO.format(release_period=release_period,
                                             release_date=release_date),
                 color='green')
Esempio n. 23
0
 def setup(self, general_config) -> tuple:
     emitter = setup_emitter(general_config)   # TODO: Restore Banner:  network=self.network.capitalize()
     registry = get_registry(network=self.network, registry_filepath=self.registry_filepath)
     blockchain = connect_to_blockchain(emitter=emitter, provider_uri=self.provider_uri)
     return emitter, registry, blockchain
Esempio n. 24
0
def divide(general_config, transacting_staker_options, config_file, force,
           value, lock_periods, index):
    """Create a new stake from part of an existing one."""

    # Setup
    emitter = setup_emitter(general_config)
    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()
    economics = STAKEHOLDER.economics
    action_period = STAKEHOLDER.staking_agent.get_current_period()

    client_account, staking_address = select_client_account_for_staking(
        emitter=emitter,
        stakeholder=STAKEHOLDER,
        staking_address=transacting_staker_options.staker_options.
        staking_address,
        individual_allocation=STAKEHOLDER.individual_allocation,
        force=force)

    # Dynamic click types (Economics)
    min_locked = economics.minimum_allowed_locked
    stake_value_range = click.FloatRange(
        min=NU.from_nunits(min_locked).to_tokens(), clamp=False)

    if transacting_staker_options.staker_options.staking_address and index is not None:  # 0 is valid.
        STAKEHOLDER.stakes = StakeList(
            registry=STAKEHOLDER.registry,
            checksum_address=transacting_staker_options.staker_options.
            staking_address)
        STAKEHOLDER.stakes.refresh()
        current_stake = STAKEHOLDER.stakes[index]
    else:
        current_stake = select_stake(stakeholder=STAKEHOLDER,
                                     emitter=emitter,
                                     divisible=True,
                                     staker_address=client_account)

    #
    # Stage Stake
    #

    # Value
    if not value:
        min_allowed_locked = NU.from_nunits(
            STAKEHOLDER.economics.minimum_allowed_locked)
        max_divide_value = max(min_allowed_locked,
                               current_stake.value - min_allowed_locked)
        prompt = PROMPT_STAKE_DIVIDE_VALUE.format(
            minimm=min_allowed_locked, maximum=str(max_divide_value))
        value = click.prompt(prompt, type=stake_value_range)
    value = NU(value, 'NU')

    # Duration
    if not lock_periods:
        max_extension = MAX_UINT16 - current_stake.final_locked_period
        divide_extension_range = click.IntRange(min=1,
                                                max=max_extension,
                                                clamp=False)
        extension = click.prompt(PROMPT_STAKE_EXTEND_VALUE,
                                 type=divide_extension_range)
    else:
        extension = lock_periods

    if not force:
        confirm_large_stake(lock_periods=extension, value=value)
        paint_staged_stake_division(emitter=emitter,
                                    stakeholder=STAKEHOLDER,
                                    original_stake=current_stake,
                                    target_value=value,
                                    extension=extension)
        click.confirm(CONFIRM_BROADCAST_STAKE_DIVIDE, abort=True)

    # Authenticate
    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    # Consistency check to prevent the above agreement from going stale.
    last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
    if action_period != last_second_current_period:
        emitter.echo(PERIOD_ADVANCED_WARNING, red='red')
        raise click.Abort

    # Execute
    STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address,
                           password=password)
    modified_stake, new_stake = STAKEHOLDER.divide_stake(
        stake_index=current_stake.index,
        target_value=value,
        additional_periods=extension)
    emitter.echo(SUCCESSFUL_STAKE_DIVIDE, color='green', verbosity=1)
    paint_receipt_summary(emitter=emitter,
                          receipt=new_stake.receipt,
                          chain_name=blockchain.client.chain_name)

    # Show the resulting stake list
    paint_stakes(emitter=emitter, stakeholder=STAKEHOLDER)
Esempio n. 25
0
def destroy(general_config, config_options, config_file, force):
    """Delete Ursula node configuration."""
    emitter = setup_emitter(general_config, config_options.worker_address)
    _pre_launch_warnings(emitter, dev=config_options.dev, force=force)
    ursula_config = config_options.create_config(emitter, config_file)
    destroy_configuration(emitter, character_config=ursula_config, force=force)
Esempio n. 26
0
def retrieve_and_decrypt(general_config, character_options, config_file,
                         alice_verifying_key, treasure_map, message_kit, ipfs,
                         alice, decode, force):
    """Obtain plaintext from encrypted data, if access was granted."""
    # 'message_kit' is a required and a "multiple" value click option  - the option name was kept singular so that
    # it makes sense when specifying many of them i.e. `--message-kit <message_kit_1> --message-kit <message_kit_2> ...`
    message_kits = list(message_kit)

    # Setup
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter,
                                             config_file,
                                             json_ipc=general_config.json_ipc)

    if not (bool(alice_verifying_key) ^ bool(alice)):
        message = f"Pass either '--alice_verifying_key' or '--alice'; " \
                  f"got {'both' if alice_verifying_key else 'neither'}"
        raise click.BadOptionUsage(
            option_name='--alice_verifying_key, --alice', message=message)

    if not alice_verifying_key:
        if alice:  # from storage
            card = Card.load(identifier=alice)
            if card.character is not Alice:
                emitter.error('Grantee card is not an Alice.')
                raise click.Abort
            alice_verifying_key = bytes(card.verifying_key).hex()
            emitter.message(
                f'{card.nickname or ("Alice #"+card.id.hex())}\n'
                f'Verifying Key  | {bytes(card.verifying_key).hex()}',
                color='green')
            if not force:
                click.confirm('Is this the correct Granter (Alice)?',
                              abort=True)

    if ipfs:
        # '--message_kit' option was repurposed to specify ipfs cids (#2098)
        cids = []
        for cid in message_kits:
            cids.append(cid)

        # populate message_kits list with actual message_kits
        message_kits = []
        import ipfshttpclient
        # TODO: #2108
        emitter.message(f"Connecting to IPFS Gateway {ipfs}")
        ipfs_client = ipfshttpclient.connect(ipfs)
        for cid in cids:
            raw_message_kit = ipfs_client.cat(
                cid)  # cat the contents at the hash reference
            emitter.message(f"Downloaded message kit from IPFS (CID {cid})",
                            color='green')
            message_kit = raw_message_kit.decode()  # cast to utf-8
            message_kits.append(message_kit)

    # Request
    bob_request_data = {
        'alice_verifying_key': alice_verifying_key,
        'message_kits': message_kits,
        'encrypted_treasure_map': treasure_map
    }

    response = BOB.controller.retrieve_and_decrypt(request=bob_request_data)
    if decode:
        messages = list(
            [b64decode(r).decode() for r in response['cleartexts']])
        emitter.echo('----------Messages----------')
        for message in messages:
            emitter.echo(message)
    return response
Esempio n. 27
0
def public_keys(general_config, character_options, config_file):
    """Obtain Bob's public verification and encryption keys."""
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter, config_file)
    response = BOB.controller.public_keys()
    return response