Esempio n. 1
0
def display_recent_txs(wallet_obj):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to find transactions related to your addresses.'))
        puts(colored.red('You may dump all your addresses while offline by selecting option 0.'))
        return

    local_tz = get_localzone()

    # Show overall balance info
    display_balance_info(wallet_obj=wallet_obj)

    mpub = wallet_obj.serialize_b58(private=False)
    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    wallet_details = get_wallet_transactions(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol_from_mkey(mpub),
            )
    verbose_print(wallet_details)

    # TODO: pagination for lots of transactions

    txs = wallet_details.get('unconfirmed_txrefs', []) + wallet_details.get('txrefs', [])

    if txs:
        for tx_object in flatten_txns_by_hash(txs, nesting=False):
            if tx_object.get('confirmed_at'):
                tx_time = tx_object['confirmed_at']
            else:
                tx_time = tx_object['received_at']
            net_satoshis_tx = sum(tx_object['txns_satoshis_list'])
            conf_str = ''
            if tx_object.get('confirmed_at'):
                if tx_object.get('confirmations'):
                    if tx_object.get('confirmations') <= 6:
                        conf_str = ' (%s confirmations)' % tx_object.get('confirmations')
                    else:
                        conf_str = ' (6+ confirmations)'
            else:
                conf_str = ' (0 confirmations!)'
            puts(colored.green('%s: %s%s %s in TX hash %s%s' % (
                tx_time.astimezone(local_tz).strftime("%Y-%m-%d %H:%M %Z"),
                '+' if net_satoshis_tx > 0 else '',
                format_crypto_units(
                    input_quantity=net_satoshis_tx,
                    input_type='satoshi',
                    output_type=UNIT_CHOICE,
                    coin_symbol=coin_symbol_from_mkey(mpub),
                    print_cs=True,
                    ),
                'received' if net_satoshis_tx > 0 else 'sent',
                tx_object['tx_hash'],
                conf_str,
                )))
    else:
        puts('No Transactions')
Esempio n. 2
0
def display_new_receiving_addresses(wallet_obj):

    if not USER_ONLINE:
        puts(
            colored.red(
                'BlockCypher connection needed to see which addresses have been used.'
            ))
        puts(
            colored.red(
                'You may dump all your addresses offline by selecting option 0.'
            ))
        return

    mpub = wallet_obj.serialize_b58(private=False)

    puts(
        'How many receiving addreses keys do you want to see (max 5 at a time)?'
    )
    puts('Enter "b" to go back.\n')

    num_addrs = get_int(
        user_prompt=DEFAULT_PROMPT,
        min_int=1,
        max_int=5,
        default_input='1',
        show_default=True,
        quit_ok=True,
    )

    if num_addrs is False:
        return

    verbose_print('num_addrs:\n%s' % num_addrs)

    unused_receiving_addresses = get_unused_receiving_addresses(
        wallet_obj=wallet_obj,
        num_addrs=num_addrs,
    )

    puts('-' * 70 + '\n')
    if num_addrs > 1:
        addr_str = 'Addresses'
    else:
        addr_str = 'Address'

    puts('Unused %s Receiving %s - (for others to send you funds):' % (
        COIN_SYMBOL_MAPPINGS[coin_symbol_from_mkey(mpub)]['currency_abbrev'],
        addr_str,
    ))

    for unused_receiving_address in unused_receiving_addresses:
        with indent(2):
            puts(
                colored.green('%s (path is %s)' % (
                    unused_receiving_address['pub_address'],
                    unused_receiving_address['path'],
                )))
Esempio n. 3
0
def display_balance_info(wallet_obj, verbose=False):
    if not USER_ONLINE:
        return

    mpub = wallet_obj.serialize_b58(private=False)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    verbose_print('Wallet Name: %s' % wallet_name)
    verbose_print('API Key: %s' % BLOCKCYPHER_API_KEY)

    coin_symbol = coin_symbol_from_mkey(mpub)

    wallet_details = get_wallet_balance(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol,
            )
    verbose_print(wallet_details)

    puts('-' * 70 + '\n')
    balance_str = 'Balance: %s' % (
            format_crypto_units(
                input_quantity=wallet_details['final_balance'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
            ))
    puts(colored.green(balance_str))
    if wallet_details['unconfirmed_balance']:
        balance_str += ' (%s%s of this is unconfirmed)' % (
                '+' if wallet_details['unconfirmed_balance'] else '',  # hack
                format_crypto_units(
                    input_quantity=wallet_details['unconfirmed_balance'],
                    input_type='satoshi',
                    output_type=UNIT_CHOICE,
                    print_cs=True,
                    coin_symbol=coin_symbol,
                ),
                )

    tx_string = 'Transactions: %s' % wallet_details['final_n_tx']
    if wallet_details['unconfirmed_n_tx']:
        tx_string += ' (%s unconfirmed)' % wallet_details['unconfirmed_n_tx']
    puts(colored.green(tx_string + '\n'))

    puts('More info:')
    puts(colored.blue(get_public_wallet_url(mpub)))
    puts()

    return wallet_details['final_balance']
Esempio n. 4
0
def display_balance_info(wallet_obj, verbose=False):
    if not USER_ONLINE:
        return

    mpub = wallet_obj.serialize_b58(private=False)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    verbose_print('Wallet Name: %s' % wallet_name)
    verbose_print('API Key: %s' % BLOCKCYPHER_API_KEY)

    coin_symbol = coin_symbol_from_mkey(mpub)

    wallet_details = get_wallet_balance(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol,
            )
    verbose_print(wallet_details)

    puts('-' * 70 + '\n')
    balance_str = 'Balance: %s' % (
            format_crypto_units(
                input_quantity=wallet_details['final_balance'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
            ))
    puts(colored.green(balance_str))
    if wallet_details['unconfirmed_balance']:
        balance_str += ' (%s%s of this is unconfirmed)' % (
                '+' if wallet_details['unconfirmed_balance'] else '',  # hack
                format_crypto_units(
                    input_quantity=wallet_details['unconfirmed_balance'],
                    input_type='satoshi',
                    output_type=UNIT_CHOICE,
                    print_cs=True,
                    coin_symbol=coin_symbol,
                ),
                )

    tx_string = 'Transactions: %s' % wallet_details['final_n_tx']
    if wallet_details['unconfirmed_n_tx']:
        tx_string += ' (%s unconfirmed)' % wallet_details['unconfirmed_n_tx']
    puts(colored.green(tx_string + '\n'))

    puts('More info:')
    puts(colored.blue(get_public_wallet_url(mpub)))
    puts()

    return wallet_details['final_balance']
Esempio n. 5
0
def register_unused_addresses(wallet_obj, subchain_index, num_addrs=1):
    '''
    Hit /derive to register new unused_addresses on a subchain_index and verify them client-side

    Returns a list of dicts of the following form:
        [
            {'address': '1abc123...', 'path': 'm/0/9', 'public': '0123456...'},
            ...,
        ]
    '''

    verbose_print(
        'register_unused_addresses called on subchain %s for %s addrs' % (
            subchain_index,
            num_addrs,
        ))

    assert type(subchain_index) is int, subchain_index
    assert type(num_addrs) is int, num_addrs
    assert num_addrs > 0

    mpub = wallet_obj.serialize_b58(private=False)
    coin_symbol = coin_symbol_from_mkey(mpub)
    wallet_name = get_blockcypher_walletname_from_mpub(
        mpub=mpub,
        subchain_indices=[0, 1],
    )
    network = guess_network_from_mkey(mpub)

    # register new address(es)
    derivation_response = derive_hd_address(
        api_key=BLOCKCYPHER_API_KEY,
        wallet_name=wallet_name,
        num_addresses=num_addrs,
        subchain_index=subchain_index,
        coin_symbol=coin_symbol,
    )

    verbose_print('derivation_response:')
    verbose_print(derivation_response)

    address_paths = derivation_response['chains'][0]['chain_addresses']

    # verify new addresses client-side
    full_address_paths = verify_and_fill_address_paths_from_bip32key(
        address_paths=address_paths,
        master_key=mpub,
        network=network,
    )

    return full_address_paths
Esempio n. 6
0
def dump_all_keys_or_addrs(wallet_obj):
    '''
    Offline-enabled mechanism to dump addresses
    '''

    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key:
        desc_str = 'private keys'
    else:
        desc_str = 'addresses'
        puts('Displaying Public Addresses Only')
        puts('For Private Keys, please open bcwallet with your Master Private Key:\n')
        priv_to_display = '%s123...' % first4mprv_from_mpub(mpub=mpub)
        print_bcwallet_basic_priv_opening(priv_to_display=priv_to_display)

    puts('How many %s (on each chain) do you want to dump?' % desc_str)

    num_keys = get_int(
            user_prompt=DEFAULT_PROMPT,
            max_int=10**5,
            default_input='5',
            show_default=True,
            )

    puts('-' * 70)
    for chain_int in (0, 1):
        for current in range(0, num_keys):
            path = "m/%d/%d" % (chain_int, current)
            if current == 0:
                if chain_int == 0:
                    print_external_chain()
                    print_key_path_header()
                elif chain_int == 1:
                    print_internal_chain()
                    print_key_path_header()
            child_wallet = wallet_obj.get_child_for_path(path)
            if wallet_obj.private_key:
                wif_to_use = child_wallet.export_to_wif()
            else:
                wif_to_use = None
            print_path_info(
                    address=child_wallet.to_address(),
                    path=path,
                    wif=wif_to_use,
                    coin_symbol=coin_symbol_from_mkey(mpub),
                    )

    puts(colored.blue('\nYou can compare this output to bip32.org'))

    print_traversal_warning()
Esempio n. 7
0
def get_addresses_on_both_chains(wallet_obj, used=None, zero_balance=None):
    '''
    Get addresses across both subchains based on the filter criteria passed in

    Returns a list of dicts of the following form:
        [
            {'address': '1abc123...', 'path': 'm/0/9', 'pubkeyhex': '0123456...'},
            ...,
        ]

    Dicts may also contain WIF and privkeyhex if wallet_obj has private key
    '''
    mpub = wallet_obj.serialize_b58(private=False)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    wallet_addresses = get_wallet_addresses(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            is_hd_wallet=True,
            used=used,
            zero_balance=zero_balance,
            coin_symbol=coin_symbol_from_mkey(mpub),
            )
    verbose_print('wallet_addresses:')
    verbose_print(wallet_addresses)

    if wallet_obj.private_key:
        master_key = wallet_obj.serialize_b58(private=True)
    else:
        master_key = mpub

    chains_address_paths_cleaned = []
    for chain in wallet_addresses['chains']:
        if chain['chain_addresses']:
            chain_address_paths = verify_and_fill_address_paths_from_bip32key(
                    address_paths=chain['chain_addresses'],
                    master_key=master_key,
                    network=guess_network_from_mkey(mpub),
                    )
            chain_address_paths_cleaned = {
                    'index': chain['index'],
                    'chain_addresses': chain_address_paths,
                    }
            chains_address_paths_cleaned.append(chain_address_paths_cleaned)

    return chains_address_paths_cleaned
Esempio n. 8
0
def register_unused_addresses(wallet_obj, subchain_index, num_addrs=1):
    '''
    Hit /derive to register new unused_addresses on a subchain_index and verify them client-side

    Returns a list of dicts of the following form:
        [
            {'address': '1abc123...', 'path': 'm/0/9', 'public': '0123456...'},
            ...,
        ]
    '''

    verbose_print('register_unused_addresses called on subchain %s for %s addrs' % (
        subchain_index,
        num_addrs,
        ))

    assert type(subchain_index) is int, subchain_index
    assert type(num_addrs) is int, num_addrs
    assert num_addrs > 0

    mpub = wallet_obj.serialize_b58(private=False)
    coin_symbol = coin_symbol_from_mkey(mpub)
    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )
    network = guess_network_from_mkey(mpub)

    # register new address(es)
    derivation_response = derive_hd_address(
            api_key=BLOCKCYPHER_API_KEY,
            wallet_name=wallet_name,
            num_addresses=num_addrs,
            subchain_index=subchain_index,
            coin_symbol=coin_symbol,
            )

    verbose_print('derivation_response:')
    verbose_print(derivation_response)

    address_paths = derivation_response['chains'][0]['chain_addresses']

    # verify new addresses client-side
    full_address_paths = verify_and_fill_address_paths_from_bip32key(
            address_paths=address_paths,
            master_key=mpub,
            network=network,
            )

    return full_address_paths
Esempio n. 9
0
def get_addresses_on_both_chains(wallet_obj, used=None, zero_balance=None):
    '''
    Get addresses across both subchains based on the filter criteria passed in

    Returns a list of dicts of the following form:
        [
            {'address': '1abc123...', 'path': 'm/0/9', 'pubkeyhex': '0123456...'},
            ...,
        ]

    Dicts may also contain WIF and privkeyhex if wallet_obj has private key
    '''
    mpub = wallet_obj.serialize_b58(private=False)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    wallet_addresses = get_wallet_addresses(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            is_hd_wallet=True,
            used=used,
            zero_balance=zero_balance,
            coin_symbol=coin_symbol_from_mkey(mpub),
            )
    verbose_print('wallet_addresses:')
    verbose_print(wallet_addresses)

    if wallet_obj.private_key:
        master_key = wallet_obj.serialize_b58(private=True)
    else:
        master_key = mpub

    chains_address_paths_cleaned = []
    for chain in wallet_addresses['chains']:
        if chain['chain_addresses']:
            chain_address_paths = verify_and_fill_address_paths_from_bip32key(
                    address_paths=chain['chain_addresses'],
                    master_key=master_key,
                    network=guess_network_from_mkey(mpub),
                    )
            chain_address_paths_cleaned = {
                    'index': chain['index'],
                    'chain_addresses': chain_address_paths,
                    }
            chains_address_paths_cleaned.append(chain_address_paths_cleaned)

    return chains_address_paths_cleaned
Esempio n. 10
0
def display_new_receiving_addresses(wallet_obj):

    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to see which addresses have been used.'))
        puts(colored.red('You may dump all your addresses offline by selecting option 0.'))
        return

    mpub = wallet_obj.serialize_b58(private=False)

    puts('How many receiving addreses keys do you want to see (max 5 at a time)?')
    puts('Enter "b" to go back.\n')

    num_addrs = get_int(
            user_prompt=DEFAULT_PROMPT,
            min_int=1,
            max_int=5,
            default_input='1',
            show_default=True,
            quit_ok=True,
            )

    if num_addrs is False:
        return

    verbose_print('num_addrs:\n%s' % num_addrs)

    unused_receiving_addresses = get_unused_receiving_addresses(
            wallet_obj=wallet_obj,
            num_addrs=num_addrs,
            )

    puts('-' * 70 + '\n')
    if num_addrs > 1:
        addr_str = 'Addresses'
    else:
        addr_str = 'Address'

    puts('Unused %s Receiving %s - (for others to send you funds):' % (
        COIN_SYMBOL_MAPPINGS[coin_symbol_from_mkey(mpub)]['currency_abbrev'],
        addr_str,
        ))

    for unused_receiving_address in unused_receiving_addresses:
        with indent(2):
            puts(colored.green('%s (path is %s)' % (
                unused_receiving_address['pub_address'],
                unused_receiving_address['path'],
                )))
Esempio n. 11
0
def send_funds(wallet_obj, change_address=None, destination_address=None, dest_satoshis=None, tx_preference=None):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to fetch unspents and broadcast signed transaction.'))
        puts(colored.red('You may dump all your addresses and private keys while offline by selecting option 0 on the home screen.'))
        return

    mpub = wallet_obj.serialize_b58(private=False)
    if not wallet_obj.private_key:
        print_pubwallet_notice(mpub=mpub)
        return

    coin_symbol = str(coin_symbol_from_mkey(mpub))
    verbose_print(coin_symbol)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )
    wallet_details = get_wallet_transactions(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol,
            )
    verbose_print(wallet_details)

    if wallet_details['final_balance'] == 0:
        puts(colored.red("0 balance. You can't send funds if you don't have them available!"))
        return

    mpriv = wallet_obj.serialize_b58(private=True)

    if not destination_address:
        display_shortname = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_shortname']
        puts('\nWhat %s address do you want to send to?' % display_shortname)
        destination_address = get_crypto_address(coin_symbol=coin_symbol, quit_ok=True)
        if destination_address is False:
            puts(colored.red('Transaction Not Broadcast!'))
            return

    if not dest_satoshis:

        crypto_units = format_crypto_units(
                input_quantity=wallet_details['final_balance'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                )
        curr_symbol = get_curr_symbol(
                coin_symbol=coin_symbol,
                output_type=UNIT_CHOICE,
                )
        puts('\nHow much (in %s) do you want to send?' % curr_symbol)
        puts('Your current balance is %s.' % crypto_units)
        puts('Note that due to small %s network transaction fees your full balance may not be available to send.' % display_shortname)
        puts('To send your full balance (less transaction fees), enter "-1" without the quotes.')

        dest_crypto_qty = get_crypto_qty(
                max_num=from_satoshis(
                    input_satoshis=wallet_details['final_balance'],
                    output_type=UNIT_CHOICE,
                    ),
                input_type=UNIT_CHOICE,
                user_prompt=DEFAULT_PROMPT,
                quit_ok=True,
                )
        if dest_crypto_qty is False:
            # user aborted with Q
            puts(colored.red('Transaction Not Broadcast!'))
            return

        if dest_crypto_qty == -1:
            dest_satoshis = -1
        else:
            dest_satoshis = to_satoshis(
                    input_quantity=dest_crypto_qty,
                    input_type=UNIT_CHOICE,
                    )

    inputs = [{
            'wallet_name': wallet_name,
            'wallet_token': BLOCKCYPHER_API_KEY,
            }, ]
    outputs = [{
            'value': dest_satoshis,
            'address': destination_address,
            }, ]

    if dest_satoshis == -1:
        sweep_funds = True
        change_address = None
    else:
        sweep_funds = False
        if not change_address:
            change_address = get_unused_change_addresses(
                    wallet_obj=wallet_obj,
                    num_addrs=1,
                    )[0]['pub_address']

    if not tx_preference:
        tx_preference = txn_preference_chooser(user_prompt=DEFAULT_PROMPT)

    verbose_print('Inputs:')
    verbose_print(inputs)
    verbose_print('Outputs:')
    verbose_print(outputs)
    verbose_print('Change Address: %s' % change_address)
    verbose_print('coin symbol: %s' % coin_symbol)
    verbose_print('TX Preference: %s' % tx_preference)

    unsigned_tx = create_unsigned_tx(
        inputs=inputs,
        outputs=outputs,
        change_address=change_address,
        preference=tx_preference,
        coin_symbol=coin_symbol,
        # will verify in the next step,
        # that way if there is an error here we can display that to user
        verify_tosigntx=False,
        include_tosigntx=True,
        )

    verbose_print('Unsigned TX:')
    verbose_print(unsigned_tx)

    if 'errors' in unsigned_tx:
        if any([x.get('error', '').startswith('Not enough funds after fees') for x in unsigned_tx['errors']]):
            puts("Sorry, after transaction fees there's not (quite) enough funds to send %s." % (
                format_crypto_units(
                    input_quantity=dest_satoshis,
                    input_type='satoshi',
                    output_type=UNIT_CHOICE,
                    coin_symbol=coin_symbol,
                    print_cs=True,
                )))
            puts('Would you like to send the max you can instead?')
            if confirm(user_prompt=DEFAULT_PROMPT, default=False):
                return send_funds(
                        wallet_obj=wallet_obj,
                        change_address=change_address,
                        destination_address=destination_address,
                        dest_satoshis=-1,  # sweep
                        tx_preference=tx_preference,
                        )
            else:
                puts(colored.red('Transaction Not Broadcast!'))
                return

        else:
            puts(colored.red('TX Error(s): Tx NOT Signed or Broadcast'))
            for error in unsigned_tx['errors']:
                puts(colored.red(error['error']))
            # Abandon
            return

    # Verify TX requested to sign is as expected
    tx_is_correct, err_msg = verify_unsigned_tx(
            unsigned_tx=unsigned_tx,
            inputs=inputs,
            outputs=outputs,
            sweep_funds=sweep_funds,
            change_address=change_address,
            coin_symbol=coin_symbol,
            )
    if not tx_is_correct:
        puts(colored.red('TX Error: Tx NOT Signed or Broadcast'))
        puts(colored.red(err_msg))
        # Abandon
        return

    input_addresses = get_input_addresses(unsigned_tx)
    verbose_print('input_addresses')
    verbose_print(input_addresses)

    address_paths = [{'path': x['hd_path'], 'address': x['addresses'][0]} for x in unsigned_tx['tx']['inputs']]

    # be sure all addresses returned
    address_paths_filled = verify_and_fill_address_paths_from_bip32key(
            address_paths=address_paths,
            master_key=mpriv,
            network=guess_network_from_mkey(mpriv),
            )

    verbose_print('adress_paths_filled:')
    verbose_print(address_paths_filled)
    hexkeypair_dict = hexkeypair_list_to_dict(address_paths_filled)

    verbose_print('hexkeypair_dict:')
    verbose_print(hexkeypair_dict)

    if len(hexkeypair_dict.keys()) != len(set(input_addresses)):
        notfound_addrs = set(input_addresses) - set(hexkeypair_dict.keys())
        err_msg = "Couldn't find %s traversing bip32 key" % notfound_addrs
        raise Exception('Traversal Fail: %s' % err_msg)

    privkeyhex_list = [hexkeypair_dict[x]['privkeyhex'] for x in input_addresses]
    pubkeyhex_list = [hexkeypair_dict[x]['pubkeyhex'] for x in input_addresses]

    verbose_print('Private Key List: %s' % privkeyhex_list)
    verbose_print('Public Key List: %s' % pubkeyhex_list)

    # sign locally
    tx_signatures = make_tx_signatures(
            txs_to_sign=unsigned_tx['tosign'],
            privkey_list=privkeyhex_list,
            pubkey_list=pubkeyhex_list,
            )
    verbose_print('TX Signatures: %s' % tx_signatures)

    # final confirmation before broadcast

    if dest_satoshis == -1:
        # remember that sweep TXs cannot verify amounts client-side (only destination addresses)
        dest_satoshis_to_display = unsigned_tx['tx']['total'] - unsigned_tx['tx']['fees']
    else:
        dest_satoshis_to_display = dest_satoshis

    CONF_TEXT = "Send %s to %s with a fee of %s (%s%% of the amount you're sending)?" % (
            format_crypto_units(
                input_quantity=dest_satoshis_to_display,
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                ),
            destination_address,
            format_crypto_units(
                input_quantity=unsigned_tx['tx']['fees'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                ),
            round(100.0 * unsigned_tx['tx']['fees'] / dest_satoshis_to_display, 4),
            )
    puts(CONF_TEXT)

    if not confirm(user_prompt=DEFAULT_PROMPT, default=True):
        puts(colored.red('Transaction Not Broadcast!'))
        return

    broadcasted_tx = broadcast_signed_transaction(
            unsigned_tx=unsigned_tx,
            signatures=tx_signatures,
            pubkeys=pubkeyhex_list,
            coin_symbol=coin_symbol,
    )
    verbose_print('Broadcast TX Details:')
    verbose_print(broadcasted_tx)

    if 'errors' in broadcasted_tx:
        puts(colored.red('TX Error(s): Tx May NOT Have Been Broadcast'))
        for error in broadcasted_tx['errors']:
            puts(colored.red(error['error']))
        return

    tx_hash = broadcasted_tx['tx']['hash']
    tx_url = get_tx_url(
            tx_hash=tx_hash,
            coin_symbol=coin_symbol,
            )
    puts(colored.green('Transaction %s Broadcast' % tx_hash))
    puts(colored.blue(tx_url))

    # Display updated wallet balance info
    display_balance_info(wallet_obj=wallet_obj)
Esempio n. 12
0
def sweep_funds_from_privkey(wallet_obj):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to fetch unspents and broadcast signed transaction.'))
        return

    mpub = wallet_obj.serialize_b58(private=False)
    coin_symbol = str(coin_symbol_from_mkey(mpub))
    network = guess_network_from_mkey(mpub)

    puts('Enter a private key (in WIF format) to send from:')
    wif_obj = get_wif_obj(network=network, user_prompt=DEFAULT_PROMPT, quit_ok=True)

    if wif_obj is False:
        return

    pkey_addr = wif_obj.get_public_key().to_address(compressed=True)

    inputs = [{
            'address': pkey_addr,
            }, ]
    verbose_print('Inputs:\n%s' % inputs)

    dest_addr = get_unused_receiving_addresses(
            wallet_obj=wallet_obj,
            num_addrs=1,
            )[0]['pub_address']

    outputs = [{
            'address': dest_addr,
            'value': -1,  # sweep value
            }, ]
    verbose_print('Outputs:\n%s' % outputs)

    unsigned_tx = create_unsigned_tx(
        inputs=inputs,
        outputs=outputs,
        change_address=None,
        coin_symbol=coin_symbol,
        # will verify in the next step,
        # that way if there is an error here we can display that to user
        verify_tosigntx=False,
        include_tosigntx=True,
        )
    verbose_print('Unsigned TX:')
    verbose_print(unsigned_tx)

    if 'errors' in unsigned_tx:
        puts(colored.red('TX Error(s): Tx NOT Signed or Broadcast'))
        for error in unsigned_tx['errors']:
            puts(colored.red(error['error']))
        # Abandon
        return

    # Verify TX requested to sign is as expected
    tx_is_correct, err_msg = verify_unsigned_tx(
            unsigned_tx=unsigned_tx,
            inputs=inputs,
            outputs=outputs,
            sweep_funds=True,
            change_address=None,
            coin_symbol=coin_symbol,
            )
    if not tx_is_correct:
        puts(colored.red('TX Error: Tx NOT Signed or Broadcast'))
        puts(colored.red(err_msg))
        # Abandon
        return

    privkeyhex_list, pubkeyhex_list = [], []
    for _ in unsigned_tx['tx']['inputs']:
        privkeyhex_list.append(wif_obj.get_key())
        pubkeyhex_list.append(wif_obj.get_public_key().get_key(
            compressed=True))
    verbose_print('Private Key List: %s' % privkeyhex_list)
    verbose_print('Public Key List: %s' % pubkeyhex_list)

    # sign locally
    tx_signatures = make_tx_signatures(
            txs_to_sign=unsigned_tx['tosign'],
            privkey_list=privkeyhex_list,
            pubkey_list=pubkeyhex_list,
            )
    verbose_print('TX Signatures: %s' % tx_signatures)

    # TODO: add final confirmation before broadcast

    broadcasted_tx = broadcast_signed_transaction(
            unsigned_tx=unsigned_tx,
            signatures=tx_signatures,
            pubkeys=pubkeyhex_list,
            coin_symbol=coin_symbol,
    )
    verbose_print('Broadcasted TX')
    verbose_print(broadcasted_tx)

    tx_hash = broadcasted_tx['tx']['hash']
    puts(colored.green('TX Broadcast: %s' % tx_hash))
    tx_url = get_tx_url(
            tx_hash=tx_hash,
            coin_symbol=coin_symbol,
            )
    puts(colored.blue(tx_url))

    # Display updated wallet balance info
    display_balance_info(wallet_obj=wallet_obj)
Esempio n. 13
0
def first4mprv_from_mpub(mpub):
    coin_symbol = coin_symbol_from_mkey(mkey=mpub)
    return COIN_SYMBOL_MAPPINGS[coin_symbol]['first4_mprv']
Esempio n. 14
0
def get_public_wallet_url(mpub):
    # subchain indices set at 0 * 1
    return 'https://live.blockcypher.com/%s/xpub/%s/?subchain-indices=0-1' % (
        coin_symbol_from_mkey(mpub),
        mpub,
    )
Esempio n. 15
0
def wallet_home(wallet_obj):
    '''
    Loaded on bootup (and stays in while loop until quitting)
    '''
    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key is None:
        print_pubwallet_notice(mpub=mpub)
    else:
        print_bcwallet_basic_pub_opening(mpub=mpub)

    puts('Note: you can quit/cancel any input by entering "q" at the prompt.')

    coin_symbol = coin_symbol_from_mkey(mpub)
    if USER_ONLINE:
        wallet_name = get_blockcypher_walletname_from_mpub(
                mpub=mpub,
                subchain_indices=[0, 1],
                )

        # Instruct blockcypher to track the wallet by pubkey
        create_hd_wallet(
                wallet_name=wallet_name,
                xpubkey=mpub,
                api_key=BLOCKCYPHER_API_KEY,
                coin_symbol=coin_symbol,
                subchain_indices=[0, 1],  # for internal and change addresses
                )

        # Display balance info
        display_balance_info(wallet_obj=wallet_obj)

    # Go to home screen
    while True:
        puts('-' * 70 + '\n')

        if coin_symbol in ('bcy', 'btc-testnet'):
            display_shortname = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_shortname']
            if coin_symbol == 'bcy':
                faucet_url = 'https://accounts.blockcypher.com/blockcypher-faucet'
            elif coin_symbol == 'btc-testnet':
                faucet_url = 'https://accounts.blockcypher.com/testnet-faucet'
            puts('Get free %s faucet coins:' % display_shortname)
            puts(colored.blue(faucet_url))
            puts()

            if coin_symbol == 'btc-testnet':
                puts('Please consider returning unused testnet coins to mwmabpJVisvti3WEP5vhFRtn3yqHRD9KNP so we can distribute them to others.\n')

        puts('What do you want to do?:')
        if not USER_ONLINE:
            puts("(since you are NOT connected to BlockCypher, many choices are disabled)")
        with indent(2):
            puts(colored.cyan('1: Show balance and transactions'))
            puts(colored.cyan('2: Show new receiving addresses'))
            puts(colored.cyan('3: Send funds (more options here)'))

        if wallet_obj.private_key:
            with indent(2):
                puts(colored.cyan('0: Dump private keys and addresses (advanced users only)'))
        else:
            with indent(2):
                puts(colored.cyan('0: Dump addresses (advanced users only)'))

        choice = choice_prompt(
                user_prompt=DEFAULT_PROMPT,
                acceptable_responses=range(0, 3+1),
                quit_ok=True,
                default_input='1',
                )
        verbose_print('Choice: %s' % choice)

        if choice in ('q', 'Q'):
            puts(colored.green('Thanks for using bcwallet!'))
            break
        elif choice == '1':
            display_recent_txs(wallet_obj=wallet_obj)
        elif choice == '2':
            display_new_receiving_addresses(wallet_obj=wallet_obj)
        elif choice == '3':
            send_chooser(wallet_obj=wallet_obj)
        elif choice == '0':
            dump_private_keys_or_addrs_chooser(wallet_obj=wallet_obj)
Esempio n. 16
0
def guess_network_from_mkey(mkey):
    cs = coin_symbol_from_mkey(mkey)
    return COIN_SYMBOL_TO_BMERCHANT_NETWORK.get(cs)
Esempio n. 17
0
def guess_network_from_mkey(mkey):
    cs = coin_symbol_from_mkey(mkey)
    return COIN_SYMBOL_TO_BMERCHANT_NETWORK.get(cs)
Esempio n. 18
0
def dump_selected_keys_or_addrs(wallet_obj, used=None, zero_balance=None):
    '''
    Works for both public key only or private key access
    '''
    if wallet_obj.private_key:
        content_str = 'private keys'
    else:
        content_str = 'addresses'

    if not USER_ONLINE:
        puts(colored.red('\nInternet connection required, would you like to dump *all* %s instead?' % (
            content_str,
            content_str,
            )))
        if confirm(user_prompt=DEFAULT_PROMPT, default=True):
            dump_all_keys_or_addrs(wallet_obj=wallet_obj)
        else:
            return

    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key is None:
        puts('Displaying Public Addresses Only')
        puts('For Private Keys, please open bcwallet with your Master Private Key:\n')
        priv_to_display = '%s123...' % first4mprv_from_mpub(mpub=mpub)

        print_bcwallet_basic_priv_opening(priv_to_display=priv_to_display)

    chain_address_objs = get_addresses_on_both_chains(
            wallet_obj=wallet_obj,
            used=used,
            zero_balance=zero_balance,
            )

    if wallet_obj.private_key and chain_address_objs:
        print_childprivkey_warning()

    addr_cnt = 0
    for chain_address_obj in chain_address_objs:
        if chain_address_obj['index'] == 0:
            print_external_chain()
        elif chain_address_obj['index'] == 1:
            print_internal_chain()
        print_key_path_header()
        for address_obj in chain_address_obj['chain_addresses']:

            print_path_info(
                    address=address_obj['pub_address'],
                    wif=address_obj['wif'],
                    path=address_obj['path'],
                    coin_symbol=coin_symbol_from_mkey(mpub),
                    )

            addr_cnt += 1

    if addr_cnt:
        puts(colored.blue('\nYou can compare this output to bip32.org'))
    else:
        puts('No matching %s in this subset. Would you like to dump *all* %s instead?' % (
            content_str,
            content_str,
            ))
        if confirm(user_prompt=DEFAULT_PROMPT, default=True):
            dump_all_keys_or_addrs(wallet_obj=wallet_obj)
Esempio n. 19
0
def sweep_funds_from_privkey(wallet_obj):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to fetch unspents and broadcast signed transaction.'))
        return

    mpub = wallet_obj.serialize_b58(private=False)
    coin_symbol = str(coin_symbol_from_mkey(mpub))
    network = guess_network_from_mkey(mpub)

    puts('Enter a private key (in WIF format) to send from:')
    puts('Enter "b" to go back.\n')
    wif_obj = get_wif_obj(network=network, user_prompt=DEFAULT_PROMPT, quit_ok=True)

    if wif_obj is False:
        return

    pkey_addr = wif_obj.get_public_key().to_address(compressed=True)

    inputs = [{
            'address': pkey_addr,
            }, ]
    verbose_print('Inputs:\n%s' % inputs)

    dest_addr = get_unused_receiving_addresses(
            wallet_obj=wallet_obj,
            num_addrs=1,
            )[0]['pub_address']

    outputs = [{
            'address': dest_addr,
            'value': -1,  # sweep value
            }, ]
    verbose_print('Outputs:\n%s' % outputs)

    unsigned_tx = create_unsigned_tx(
        inputs=inputs,
        outputs=outputs,
        change_address=None,
        coin_symbol=coin_symbol,
        # will verify in the next step,
        # that way if there is an error here we can display that to user
        verify_tosigntx=False,
        include_tosigntx=True,
        )
    verbose_print('Unsigned TX:')
    verbose_print(unsigned_tx)

    if 'errors' in unsigned_tx:
        puts(colored.red('TX Error(s): Tx NOT Signed or Broadcast'))
        for error in unsigned_tx['errors']:
            puts(colored.red(error['error']))
        # Abandon
        return

    # Verify TX requested to sign is as expected
    tx_is_correct, err_msg = verify_unsigned_tx(
            unsigned_tx=unsigned_tx,
            inputs=inputs,
            outputs=outputs,
            sweep_funds=True,
            change_address=None,
            coin_symbol=coin_symbol,
            )
    if not tx_is_correct:
        puts(colored.red('TX Error: Tx NOT Signed or Broadcast'))
        puts(colored.red(err_msg))
        # Abandon
        return

    privkeyhex_list, pubkeyhex_list = [], []
    for _ in unsigned_tx['tx']['inputs']:
        privkeyhex_list.append(wif_obj.get_key())
        pubkeyhex_list.append(wif_obj.get_public_key().get_key(
            compressed=True))
    verbose_print('Private Key List: %s' % privkeyhex_list)
    verbose_print('Public Key List: %s' % pubkeyhex_list)

    # sign locally
    tx_signatures = make_tx_signatures(
            txs_to_sign=unsigned_tx['tosign'],
            privkey_list=privkeyhex_list,
            pubkey_list=pubkeyhex_list,
            )
    verbose_print('TX Signatures: %s' % tx_signatures)

    # TODO: add final confirmation before broadcast

    broadcasted_tx = broadcast_signed_transaction(
            unsigned_tx=unsigned_tx,
            signatures=tx_signatures,
            pubkeys=pubkeyhex_list,
            coin_symbol=coin_symbol,
    )
    verbose_print('Broadcasted TX')
    verbose_print(broadcasted_tx)

    tx_hash = broadcasted_tx['tx']['hash']
    puts(colored.green('TX Broadcast: %s' % tx_hash))
    tx_url = get_tx_url(
            tx_hash=tx_hash,
            coin_symbol=coin_symbol,
            )
    puts(colored.blue(tx_url))

    # Display updated wallet balance info
    display_balance_info(wallet_obj=wallet_obj)
Esempio n. 20
0
def dump_all_keys_or_addrs(wallet_obj):
    '''
    Offline-enabled mechanism to dump addresses
    '''

    print_traversal_warning()

    puts('\nDo you understand this warning?')
    if not confirm(user_prompt=DEFAULT_PROMPT, default=False):
        puts(colored.red('Dump Cancelled!'))
        return

    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key:
        desc_str = 'private keys'
    else:
        desc_str = 'addresses'
        puts('Displaying Public Addresses Only')
        puts('For Private Keys, please open bcwallet with your Master Private Key:\n')
        priv_to_display = '%s123...' % first4mprv_from_mpub(mpub=mpub)
        print_bcwallet_basic_priv_opening(priv_to_display=priv_to_display)

    puts('How many %s (on each chain) do you want to dump?' % desc_str)
    puts('Enter "b" to go back.\n')

    num_keys = get_int(
            user_prompt=DEFAULT_PROMPT,
            max_int=10**5,
            default_input='5',
            show_default=True,
            quit_ok=True,
            )

    if num_keys is False:
        return

    if wallet_obj.private_key:
        print_childprivkey_warning()

    puts('-' * 70)
    for chain_int in (0, 1):
        for current in range(0, num_keys):
            path = "m/%d/%d" % (chain_int, current)
            if current == 0:
                if chain_int == 0:
                    print_external_chain()
                    print_key_path_header()
                elif chain_int == 1:
                    print_internal_chain()
                    print_key_path_header()
            child_wallet = wallet_obj.get_child_for_path(path)
            if wallet_obj.private_key:
                wif_to_use = child_wallet.export_to_wif()
            else:
                wif_to_use = None
            print_path_info(
                    address=child_wallet.to_address(),
                    path=path,
                    wif=wif_to_use,
                    coin_symbol=coin_symbol_from_mkey(mpub),
                    )

    puts(colored.blue('\nYou can compare this output to bip32.org'))
Esempio n. 21
0
def send_funds(wallet_obj, change_address=None, destination_address=None, dest_satoshis=None, tx_preference=None):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to fetch unspents and broadcast signed transaction.'))
        puts(colored.red('You may dump all your addresses and private keys while offline by selecting option 0 on the home screen.'))
        return

    mpub = wallet_obj.serialize_b58(private=False)
    if not wallet_obj.private_key:
        print_pubwallet_notice(mpub=mpub)
        return

    coin_symbol = str(coin_symbol_from_mkey(mpub))
    verbose_print(coin_symbol)

    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )
    wallet_details = get_wallet_transactions(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol,
            )
    verbose_print(wallet_details)

    if wallet_details['final_balance'] == 0:
        puts(colored.red("0 balance. You can't send funds if you don't have them available!"))
        return

    mpriv = wallet_obj.serialize_b58(private=True)

    if not destination_address:
        display_shortname = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_shortname']
        puts('\nWhat %s address do you want to send to?' % display_shortname)
        puts('Enter "b" to go back.\n')
        destination_address = get_crypto_address(coin_symbol=coin_symbol, quit_ok=True)
        if destination_address is False:
            puts(colored.red('Transaction Not Broadcast!'))
            return

    if not dest_satoshis:

        crypto_units = format_crypto_units(
                input_quantity=wallet_details['final_balance'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                )
        curr_symbol = get_curr_symbol(
                coin_symbol=coin_symbol,
                output_type=UNIT_CHOICE,
                )
        puts('\nHow much (in %s) do you want to send?' % curr_symbol)
        puts('Your current balance is %s.' % crypto_units)
        puts('Note that due to small %s network transaction fees your full balance may not be available to send.' % display_shortname)
        puts('To send your full balance (less transaction fees), enter "-1".')
        puts('Enter "b" to go back.\n')

        dest_crypto_qty = get_crypto_qty(
                max_num=from_satoshis(
                    input_satoshis=wallet_details['final_balance'],
                    output_type=UNIT_CHOICE,
                    ),
                input_type=UNIT_CHOICE,
                user_prompt=DEFAULT_PROMPT,
                quit_ok=True,
                )
        if dest_crypto_qty is False:
            # user aborted with Q
            puts(colored.red('Transaction Not Broadcast!'))
            return

        if dest_crypto_qty == -1:
            dest_satoshis = -1
        else:
            dest_satoshis = to_satoshis(
                    input_quantity=dest_crypto_qty,
                    input_type=UNIT_CHOICE,
                    )

    inputs = [{
            'wallet_name': wallet_name,
            'wallet_token': BLOCKCYPHER_API_KEY,
            }, ]
    outputs = [{
            'value': dest_satoshis,
            'address': destination_address,
            }, ]

    if dest_satoshis == -1:
        sweep_funds = True
        change_address = None
    else:
        sweep_funds = False
        if not change_address:
            change_address = get_unused_change_addresses(
                    wallet_obj=wallet_obj,
                    num_addrs=1,
                    )[0]['pub_address']

    if not tx_preference:
        tx_preference = txn_preference_chooser(user_prompt=DEFAULT_PROMPT)

    verbose_print('Inputs:')
    verbose_print(inputs)
    verbose_print('Outputs:')
    verbose_print(outputs)
    verbose_print('Change Address: %s' % change_address)
    verbose_print('coin symbol: %s' % coin_symbol)
    verbose_print('TX Preference: %s' % tx_preference)

    unsigned_tx = create_unsigned_tx(
        inputs=inputs,
        outputs=outputs,
        change_address=change_address,
        preference=tx_preference,
        coin_symbol=coin_symbol,
        # will verify in the next step,
        # that way if there is an error here we can display that to user
        verify_tosigntx=False,
        include_tosigntx=True,
        )

    verbose_print('Unsigned TX:')
    verbose_print(unsigned_tx)

    if 'errors' in unsigned_tx:
        if any([x.get('error', '').startswith('Not enough funds after fees') for x in unsigned_tx['errors']]):
            puts("Sorry, after transaction fees there's not (quite) enough funds to send %s." % (
                format_crypto_units(
                    input_quantity=dest_satoshis,
                    input_type='satoshi',
                    output_type=UNIT_CHOICE,
                    coin_symbol=coin_symbol,
                    print_cs=True,
                )))
            puts('Would you like to send the max you can instead?')
            if confirm(user_prompt=DEFAULT_PROMPT, default=False):
                return send_funds(
                        wallet_obj=wallet_obj,
                        change_address=change_address,
                        destination_address=destination_address,
                        dest_satoshis=-1,  # sweep
                        tx_preference=tx_preference,
                        )
            else:
                puts(colored.red('Transaction Not Broadcast!'))
                return

        else:
            puts(colored.red('TX Error(s): Tx NOT Signed or Broadcast'))
            for error in unsigned_tx['errors']:
                puts(colored.red(error['error']))
            # Abandon
            return

    # Verify TX requested to sign is as expected
    tx_is_correct, err_msg = verify_unsigned_tx(
            unsigned_tx=unsigned_tx,
            inputs=inputs,
            outputs=outputs,
            sweep_funds=sweep_funds,
            change_address=change_address,
            coin_symbol=coin_symbol,
            )
    if not tx_is_correct:
        puts(colored.red('TX Error: Tx NOT Signed or Broadcast'))
        puts(colored.red(err_msg))
        # Abandon
        return

    input_addresses = get_input_addresses(unsigned_tx)
    verbose_print('input_addresses')
    verbose_print(input_addresses)

    address_paths = [{'path': x['hd_path'], 'address': x['addresses'][0]} for x in unsigned_tx['tx']['inputs']]

    # be sure all addresses returned
    address_paths_filled = verify_and_fill_address_paths_from_bip32key(
            address_paths=address_paths,
            master_key=mpriv,
            network=guess_network_from_mkey(mpriv),
            )

    verbose_print('adress_paths_filled:')
    verbose_print(address_paths_filled)
    hexkeypair_dict = hexkeypair_list_to_dict(address_paths_filled)

    verbose_print('hexkeypair_dict:')
    verbose_print(hexkeypair_dict)

    if len(hexkeypair_dict.keys()) != len(set(input_addresses)):
        notfound_addrs = set(input_addresses) - set(hexkeypair_dict.keys())
        err_msg = "Couldn't find %s traversing bip32 key" % notfound_addrs
        raise Exception('Traversal Fail: %s' % err_msg)

    privkeyhex_list = [hexkeypair_dict[x]['privkeyhex'] for x in input_addresses]
    pubkeyhex_list = [hexkeypair_dict[x]['pubkeyhex'] for x in input_addresses]

    verbose_print('Private Key List: %s' % privkeyhex_list)
    verbose_print('Public Key List: %s' % pubkeyhex_list)

    # sign locally
    tx_signatures = make_tx_signatures(
            txs_to_sign=unsigned_tx['tosign'],
            privkey_list=privkeyhex_list,
            pubkey_list=pubkeyhex_list,
            )
    verbose_print('TX Signatures: %s' % tx_signatures)

    # final confirmation before broadcast

    if dest_satoshis == -1:
        # remember that sweep TXs cannot verify amounts client-side (only destination addresses)
        dest_satoshis_to_display = unsigned_tx['tx']['total'] - unsigned_tx['tx']['fees']
    else:
        dest_satoshis_to_display = dest_satoshis

    CONF_TEXT = "Send %s to %s with a fee of %s (%s%% of the amount you're sending)?" % (
            format_crypto_units(
                input_quantity=dest_satoshis_to_display,
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                ),
            destination_address,
            format_crypto_units(
                input_quantity=unsigned_tx['tx']['fees'],
                input_type='satoshi',
                output_type=UNIT_CHOICE,
                coin_symbol=coin_symbol,
                print_cs=True,
                ),
            round(100.0 * unsigned_tx['tx']['fees'] / dest_satoshis_to_display, 4),
            )
    puts(CONF_TEXT)

    if not confirm(user_prompt=DEFAULT_PROMPT, default=True):
        puts(colored.red('Transaction Not Broadcast!'))
        return

    broadcasted_tx = broadcast_signed_transaction(
            unsigned_tx=unsigned_tx,
            signatures=tx_signatures,
            pubkeys=pubkeyhex_list,
            coin_symbol=coin_symbol,
    )
    verbose_print('Broadcast TX Details:')
    verbose_print(broadcasted_tx)

    if 'errors' in broadcasted_tx:
        puts(colored.red('TX Error(s): Tx May NOT Have Been Broadcast'))
        for error in broadcasted_tx['errors']:
            puts(colored.red(error['error']))
        return

    tx_hash = broadcasted_tx['tx']['hash']
    tx_url = get_tx_url(
            tx_hash=tx_hash,
            coin_symbol=coin_symbol,
            )
    puts(colored.green('Transaction %s Broadcast' % tx_hash))
    puts(colored.blue(tx_url))

    # Display updated wallet balance info
    display_balance_info(wallet_obj=wallet_obj)
Esempio n. 22
0
def wallet_home(wallet_obj):
    '''
    Loaded on bootup (and loops until quitting)
    '''
    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key is None:
        print_pubwallet_notice(mpub=mpub)
    else:
        puts("You've opened your wallet in PRIVATE key mode, so you CAN sign transactions.")
        puts("If you like, you can always open your wallet in PUBLIC key mode like this:\n")
        print_bcwallet_basic_pub_opening(mpub=mpub)

    coin_symbol = coin_symbol_from_mkey(mpub)
    if USER_ONLINE:
        wallet_name = get_blockcypher_walletname_from_mpub(
                mpub=mpub,
                subchain_indices=[0, 1],
                )

        # Instruct blockcypher to track the wallet by pubkey
        create_hd_wallet(
                wallet_name=wallet_name,
                xpubkey=mpub,
                api_key=BLOCKCYPHER_API_KEY,
                coin_symbol=coin_symbol,
                subchain_indices=[0, 1],  # for internal and change addresses
                )

        # Display balance info
        display_balance_info(wallet_obj=wallet_obj)

    # Go to home screen
    while True:
        puts('-' * 70 + '\n')

        if coin_symbol in ('bcy', 'btc-testnet'):
            display_shortname = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_shortname']
            if coin_symbol == 'bcy':
                faucet_url = 'https://accounts.blockcypher.com/blockcypher-faucet'
            elif coin_symbol == 'btc-testnet':
                faucet_url = 'https://accounts.blockcypher.com/testnet-faucet'
            puts(colored.blue('Get free %s faucet coins at %s\n' % (
                display_shortname,
                faucet_url,
                )))

        puts('What do you want to do?:')
        if not USER_ONLINE:
            puts("(since you are NOT connected to BlockCypher, many choices are disabled)")
        with indent(2):
            puts(colored.cyan('1: Show balance and transactions'))
            puts(colored.cyan('2: Show new receiving addresses'))
            puts(colored.cyan('3: Send funds (more options here)'))

        if wallet_obj.private_key:
            with indent(2):
                puts(colored.cyan('0: Dump private keys and addresses (advanced users only)'))
        else:
            with indent(2):
                puts(colored.cyan('0: Dump addresses (advanced users only)'))

        choice = choice_prompt(
                user_prompt=DEFAULT_PROMPT,
                acceptable_responses=range(0, 3+1),
                quit_ok=True,
                default_input='1',
                )
        verbose_print('Choice: %s' % choice)

        if choice in ('q', 'Q'):
            puts(colored.green('Thanks for using bcwallet!'))
            break
        elif choice == '1':
            display_recent_txs(wallet_obj=wallet_obj)
        elif choice == '2':
            display_new_receiving_addresses(wallet_obj=wallet_obj)
        elif choice == '3':
            send_chooser(wallet_obj=wallet_obj)
        elif choice == '0':
            dump_private_keys_or_addrs_chooser(wallet_obj=wallet_obj)
Esempio n. 23
0
def get_public_wallet_url(mpub):
    # subchain indices set at 0 * 1
    return 'https://live.blockcypher.com/%s/xpub/%s/?subchain-indices=0-1' % (
            coin_symbol_from_mkey(mpub),
            mpub,
            )
Esempio n. 24
0
def first4mprv_from_mpub(mpub):
    coin_symbol = coin_symbol_from_mkey(mkey=mpub)
    return COIN_SYMBOL_MAPPINGS[coin_symbol]['first4_mprv']
Esempio n. 25
0
def wallet_home(wallet_obj):
    '''
    Loaded on bootup (and stays in while loop until quitting)
    '''
    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key is None:
        print_pubwallet_notice(mpub=mpub)
    else:
        print_bcwallet_basic_pub_opening(mpub=mpub)

    coin_symbol = coin_symbol_from_mkey(mpub)
    if USER_ONLINE:
        wallet_name = get_blockcypher_walletname_from_mpub(
                mpub=mpub,
                subchain_indices=[0, 1],
                )

        # Instruct blockcypher to track the wallet by pubkey
        create_hd_wallet(
                wallet_name=wallet_name,
                xpubkey=mpub,
                api_key=BLOCKCYPHER_API_KEY,
                coin_symbol=coin_symbol,
                subchain_indices=[0, 1],  # for internal and change addresses
                )

        # Display balance info
        display_balance_info(wallet_obj=wallet_obj)

    # Go to home screen
    while True:
        puts('-' * 70 + '\n')

        if coin_symbol in ('bcy', 'btc-testnet'):
            display_shortname = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_shortname']
            if coin_symbol == 'bcy':
                faucet_url = 'https://accounts.blockcypher.com/blockcypher-faucet'
            elif coin_symbol == 'btc-testnet':
                faucet_url = 'https://accounts.blockcypher.com/testnet-faucet'
            puts('Get free %s faucet coins:' % display_shortname)
            puts(colored.blue(faucet_url))
            puts()

            if coin_symbol == 'btc-testnet':
                puts('Please consider returning unused testnet coins to mwmabpJVisvti3WEP5vhFRtn3yqHRD9KNP so we can distribute them to others.\n')

        puts('What do you want to do?:')
        if not USER_ONLINE:
            puts("(since you are NOT connected to BlockCypher, many choices are disabled)")
        with indent(2):
            puts(colored.cyan('1: Show balance and transactions'))
            puts(colored.cyan('2: Show new receiving addresses'))
            puts(colored.cyan('3: Send funds (more options here)'))

        with indent(2):
            if wallet_obj.private_key:
                puts(colored.cyan('0: Dump private keys and addresses (advanced users only)'))
            else:
                puts(colored.cyan('0: Dump addresses (advanced users only)'))

            puts(colored.cyan('\nq: Quit bcwallet\n'))

        choice = choice_prompt(
                user_prompt=DEFAULT_PROMPT,
                acceptable_responses=range(0, 3+1),
                quit_ok=True,
                default_input='1',
                )
        verbose_print('Choice: %s' % choice)

        if choice is False:
            puts(colored.green('Thanks for using bcwallet!'))
            print_keys_not_saved()
            break
        elif choice == '1':
            display_recent_txs(wallet_obj=wallet_obj)
        elif choice == '2':
            display_new_receiving_addresses(wallet_obj=wallet_obj)
        elif choice == '3':
            send_chooser(wallet_obj=wallet_obj)
        elif choice == '0':
            dump_private_keys_or_addrs_chooser(wallet_obj=wallet_obj)
Esempio n. 26
0
def dump_selected_keys_or_addrs(wallet_obj, used=None, zero_balance=None):
    '''
    Works for both public key only or private key access
    '''
    if wallet_obj.private_key:
        content_str = 'private keys'
    else:
        content_str = 'addresses'

    if not USER_ONLINE:
        puts(colored.red('\nInternet connection required, would you like to dump *all* %s instead?' % (
            content_str,
            content_str,
            )))
        if confirm(user_prompt=DEFAULT_PROMPT, default=True):
            dump_all_keys_or_addrs(wallet_obj=wallet_obj)
        else:
            return

    mpub = wallet_obj.serialize_b58(private=False)

    if wallet_obj.private_key is None:
        puts('Displaying Public Addresses Only')
        puts('For Private Keys, please open bcwallet with your Master Private Key:\n')
        priv_to_display = '%s123...' % first4mprv_from_mpub(mpub=mpub)

        print_bcwallet_basic_priv_opening(priv_to_display=priv_to_display)

    chain_address_objs = get_addresses_on_both_chains(
            wallet_obj=wallet_obj,
            used=used,
            zero_balance=zero_balance,
            )

    if wallet_obj.private_key and chain_address_objs:
        print_childprivkey_warning()

    addr_cnt = 0
    for chain_address_obj in chain_address_objs:
        if chain_address_obj['index'] == 0:
            print_external_chain()
        elif chain_address_obj['index'] == 1:
            print_internal_chain()
        print_key_path_header()
        for address_obj in chain_address_obj['chain_addresses']:

            print_path_info(
                    address=address_obj['pub_address'],
                    wif=address_obj['wif'],
                    path=address_obj['path'],
                    coin_symbol=coin_symbol_from_mkey(mpub),
                    )

            addr_cnt += 1

    if addr_cnt:
        puts(colored.blue('\nYou can compare this output to bip32.org'))
    else:
        puts('No matching %s in this subset. Would you like to dump *all* %s instead?' % (
            content_str,
            content_str,
            ))
        if confirm(user_prompt=DEFAULT_PROMPT, default=True):
            dump_all_keys_or_addrs(wallet_obj=wallet_obj)
Esempio n. 27
0
def display_recent_txs(wallet_obj):
    if not USER_ONLINE:
        puts(colored.red('BlockCypher connection needed to find transactions related to your addresses.'))
        puts(colored.red('You may dump all your addresses while offline by selecting option 0.'))
        return

    local_tz = get_localzone()

    # Show overall balance info
    display_balance_info(wallet_obj=wallet_obj)

    mpub = wallet_obj.serialize_b58(private=False)
    wallet_name = get_blockcypher_walletname_from_mpub(
            mpub=mpub,
            subchain_indices=[0, 1],
            )

    wallet_details = get_wallet_transactions(
            wallet_name=wallet_name,
            api_key=BLOCKCYPHER_API_KEY,
            coin_symbol=coin_symbol_from_mkey(mpub),
            )
    verbose_print(wallet_details)

    # TODO: pagination for lots of transactions

    txs = wallet_details.get('unconfirmed_txrefs', []) + wallet_details.get('txrefs', [])

    if txs:
        for tx_object in flatten_txns_by_hash(txs, nesting=False):
            if tx_object.get('confirmed_at'):
                tx_time = tx_object['confirmed_at']
            else:
                tx_time = tx_object['received_at']
            net_satoshis_tx = sum(tx_object['txns_satoshis_list'])
            conf_str = ''
            has_confirmations = False
            if tx_object.get('confirmed_at'):
                if tx_object.get('confirmations'):
                    has_confirmations = True
                    if tx_object.get('confirmations') <= 6:
                        conf_str = ' (%s confirmations)' % tx_object.get('confirmations')
                    else:
                        conf_str = ' (6+ confirmations)'
            else:
                conf_str = ' (0 confirmations!)'
            print_str = '%s: %s%s %s in TX hash %s%s' % (
                    tx_time.astimezone(local_tz).strftime("%Y-%m-%d %H:%M %Z"),
                    '+' if net_satoshis_tx > 0 else '',
                    format_crypto_units(
                        input_quantity=net_satoshis_tx,
                        input_type='satoshi',
                        output_type=UNIT_CHOICE,
                        coin_symbol=coin_symbol_from_mkey(mpub),
                        print_cs=True,
                        ),
                    'received' if net_satoshis_tx > 0 else 'sent',
                    tx_object['tx_hash'],
                    conf_str,
                    )
            if has_confirmations:
                puts(colored.green(print_str))
            else:
                puts(colored.yellow(print_str))
    else:
        puts('No Transactions')