Exemplo n.º 1
0
def generate_bitcoin_core_wallet(example_addrs, account_num):
    # Generate the data for an RPC command to import keys into Bitcoin Core
    # - yields dicts for json purposes
    from descriptor import append_checksum
    from main import settings
    import ustruct

    from public_constants import AF_P2WPKH

    chain = chains.current_chain()

    derive = "84'/{coin_type}'/{account}'".format(account=account_num,
                                                  coin_type=chain.b44_cointype)

    with stash.SensitiveValues() as sv:
        prefix = sv.derive_path(derive)
        xpub = chain.serialize_public(prefix)

        for i in range(3):
            sp = '0/%d' % i
            node = sv.derive_path(sp, master=prefix)
            a = chain.address(node, AF_P2WPKH)
            example_addrs.append(('m/%s/%s' % (derive, sp), a))

    xfp = settings.get('xfp')
    txt_xfp = xfp2str(xfp).lower()

    chain = chains.current_chain()

    _, vers, _ = version.get_mpy_version()

    for internal in [False, True]:
        desc = "wpkh([{fingerprint}/{derive}]{xpub}/{change}/*)".format(
            derive=derive.replace("'", "h"),
            fingerprint=txt_xfp,
            coin_type=chain.b44_cointype,
            account=0,
            xpub=xpub,
            change=(1 if internal else 0))

        yield {
            'desc': append_checksum(desc),
            'range': [0, 1000],
            'timestamp': 'now',
            'internal': internal,
            'keypool': True,
            'watchonly': True
        }
Exemplo n.º 2
0
async def electrum_skeleton(*A):
    # save xpub, and some other public details into a file
    import chains

    ch = chains.current_chain()

    if not await ux_show_story('''\
This saves a skeleton Electrum wallet file onto the MicroSD card. \
You can then open that file in Electrum without ever connecting this Coldcard to a computer.\n
Choose an address type for the wallet on the next screen.
''' + SENSITIVE_NOT_SECRET):
        return

    # pick segwit or classic derivation+such
    from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH
    from menu import MenuSystem, MenuItem

    # Ordering and terminology from similar screen in Electrum. I prefer
    # 'classic' instead of 'legacy' personallly.
    rv = []

    if AF_CLASSIC in ch.slip132:
        rv.append(MenuItem("Legacy (P2PKH)", f=electrum_skeleton_step2, arg=AF_CLASSIC))
    if AF_P2WPKH_P2SH in ch.slip132:
        rv.append(MenuItem("P2SH-Segwit", f=electrum_skeleton_step2, arg=AF_P2WPKH_P2SH))
    if AF_P2WPKH in ch.slip132:
        rv.append(MenuItem("Native Segwit", f=electrum_skeleton_step2, arg=AF_P2WPKH))

    return MenuSystem(rv)
Exemplo n.º 3
0
    def make_msg(start):
        msg = ''
        if start == 0:
            msg = "Press 1 to save to MicroSD."
            if version.has_fatram:
                msg += " 4 to view QR Codes."
            msg += '\n\n'
        msg += "Addresses %d..%d:\n\n" % (start, start + n - 1)

        addrs = []
        chain = chains.current_chain()

        dis.fullscreen('Wait...')

        with stash.SensitiveValues() as sv:

            for idx in range(start, start + n):
                subpath = path.format(account=0, change=0, idx=idx)
                node = sv.derive_path(subpath, register=False)
                addr = chain.address(node, addr_fmt)
                addrs.append(addr)

                msg += "%s =>\n%s\n\n" % (subpath, addr)

                dis.progress_bar_show(idx / n)

            stash.blank_object(node)

        msg += "Press 9 to see next group, 7 to go back. X to quit."

        return msg, addrs
    def make_msg(start):
        msg = ''
        if start == 0:
            msg = "Press 1 to save to MicroSD."
            msg += '\n\n'
        msg += "Addresses %d..%d:\n\n" % (start, start + n - 1)

        addrs = []
        chain = chains.current_chain()

        dis.fullscreen('Loading...')

        with stash.SensitiveValues() as sv:

            for idx in range(start, start + n):
                subpath = path.format(account=0, change=0, idx=idx)
                node = sv.derive_path(subpath, register=False)
                addr = chain.address(node, addr_fmt)
                addr1 = addr[:16]
                addr2 = addr[16:]
                addrs.append(addr)

                msg += "%s =>\n  %s\n  %s\n\n" % (subpath, addr1, addr2)

                dis.progress_bar_show(idx / n)

            stash.blank_object(node)

        msg += "Press 9 to see next group.\nPress 7 to see prev. group."

        return msg, addrs
Exemplo n.º 5
0
def generate_public_contents():
    # Generate public details about wallet.
    #
    # simple text format:
    #   key = value
    # or #comments
    # but value is JSON
    from main import settings

    num_rx = 5

    chain = chains.current_chain()

    with stash.SensitiveValues() as sv:

        yield ('''\
# Coldcard Wallet Summary File

## Wallet operates on blockchain: {nb}

For BIP44, this is coin_type '{ct}', and internally we use symbol {sym} for this blockchain.

## Top-level, 'master' extended public key ('m/'):

{xpub}

Derived public keys, as may be needed for different systems:


'''.format(nb=chain.name,
           xpub=chain.serialize_public(sv.node),
           sym=chain.ctype,
           ct=chain.b44_cointype))

        for name, path, addr_fmt in chains.CommonDerivations:

            if '{coin_type}' in path:
                path = path.replace('{coin_type}', str(chain.b44_cointype))

            yield ('''## For {name}: {path}\n\n'''.format(name=name,
                                                          path=path))

            submaster, kids = path.split('/{', 1)
            kids = '{' + kids

            node = sv.derive_path(submaster)

            yield ("%s => %s\n" % (submaster, chain.serialize_public(node)))

            yield (
                '''\n... first %d receive addresses (account=0, change=0):\n\n'''
                % num_rx)

            for i in range(num_rx):
                subpath = kids.format(account=0, change=0, idx=i)
                kid = sv.derive_path(subpath, node)
                yield ('%s/%s => %s\n' %
                       (submaster, subpath, chain.address(kid, addr_fmt)))

            yield ('\n\n')
Exemplo n.º 6
0
def generate_wasabi_wallet():
    # Generate the data for a JSON file which Wasabi can open directly as a new wallet.
    from common import settings
    import ustruct
    import version

    # bitcoin (xpub) is used, even for testnet case (ie. no tpub)
    # - altho, doesn't matter; the wallet operates based on it's own settings for test/mainnet
    #   regardless of the contents of the wallet file
    btc = chains.BitcoinMain

    with stash.SensitiveValues() as sv:
        xpub = btc.serialize_public(sv.derive_path("84'/0'/0'"))

    xfp = settings.get('xfp')
    txt_xfp = xfp2str(xfp)

    chain = chains.current_chain()
    assert chain.ctype in {'BTC', 'TBTC'}, "Only Bitcoin supported"

    _, vers, _ = version.get_mpy_version()

    return dict(MasterFingerprint=txt_xfp,
                ColdCardFirmwareVersion=vers,
                ExtPubKey=xpub)
Exemplo n.º 7
0
    async def interact(self):
        # Prompt user w/ details and get approval
        from main import dis, hsm_active

        if hsm_active:
            ch = await hsm_active.approve_msg_sign(self.text, self.address, self.subpath)
        else:
            story = MSG_SIG_TEMPLATE.format(msg=self.text, addr=self.address, subpath=self.subpath)
            ch = await ux_show_story(story)

        if ch != 'y':
            # they don't want to!
            self.refused = True
        else:

            # perform signing (progress bar shown)
            digest = chains.current_chain().hash_message(self.text.encode())
            self.result = sign_message_digest(digest, self.subpath, "Signing...")

            if self.approved_cb:
                # for micro sd case
                await self.approved_cb(self.result, self.address)

        if self.approved_cb:
            # don't kill menu depth for file case
            UserAuthorizedAction.cleanup()
            self.pop_menu()
        else:
            self.done()
Exemplo n.º 8
0
def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0):
    # Produce CSV file contents as a generator

    if ms_wallet:
        from ubinascii import hexlify as b2a_hex

        # For multisig, include redeem script and derivation for each signer
        yield '"' + '","'.join(['Index', 'Payment Address',
                                    'Redeem Script (%d of %d)' % (ms_wallet.M, ms_wallet.N)] 
                                    + (['Derivation'] * ms_wallet.N)) + '"\n'

        for (idx, derivs, addr, script) in ms_wallet.yield_addresses(start, n):
            ln = '%d,"%s","%s","' % (idx, addr, b2a_hex(script).decode())
            ln += '","'.join(derivs)
            ln += '"\n'

            yield ln

        return

    yield '"Index","Payment Address","Derivation"\n'
    ch = chains.current_chain()

    with stash.SensitiveValues() as sv:
        for idx in range(start, start+n):
            deriv = path.format(account=account_num, change=0, idx=idx)
            node = sv.derive_path(deriv, register=False)

            yield '%d,"%s","%s"\n' % (idx, ch.address(node, addr_fmt), deriv)

        stash.blank_object(node)
Exemplo n.º 9
0
async def electrum_skeleton(*a):
    # save xpub, and some other public details into a file: NOT MULTISIG

    if not await ux_show_story(electrum_export_story()):
        return

    import chains
    ch = chains.current_chain()

    # pick segwit or classic derivation+such
    from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH
    from menu import MenuSystem, MenuItem

    # Ordering and terminology from similar screen in Electrum. I prefer
    # 'classic' instead of 'legacy' personallly.
    rv = []

    if AF_CLASSIC in ch.slip132:
        rv.append(
            MenuItem("Legacy (P2PKH)",
                     f=electrum_skeleton_step2,
                     arg=AF_CLASSIC))
    if AF_P2WPKH_P2SH in ch.slip132:
        rv.append(
            MenuItem("P2SH-Segwit",
                     f=electrum_skeleton_step2,
                     arg=AF_P2WPKH_P2SH))
    if AF_P2WPKH in ch.slip132:
        rv.append(
            MenuItem("Native Segwit", f=electrum_skeleton_step2,
                     arg=AF_P2WPKH))

    return MenuSystem(rv)
Exemplo n.º 10
0
async def view_seed_words(*a):
    import stash, tcc

    if not await ux_confirm(
            '''The next screen will show the seed words (and if defined, your BIP39 passphrase).\n\nAnyone with knowledge of those words can control all funds in this wallet.'''
    ):
        return

    with stash.SensitiveValues() as sv:
        if sv.mode == 'words':
            words = tcc.bip39.from_data(sv.raw).split(' ')

            msg = 'Seed words (%d):\n' % len(words)
            msg += '\n'.join('%2d: %s' % (i + 1, w)
                             for i, w in enumerate(words))

            pw = stash.bip39_passphrase
            if pw:
                msg += '\n\nBIP39 Passphrase:\n%s' % stash.bip39_passphrase
        elif sv.mode == 'xprv':
            import chains
            msg = chains.current_chain().serialize_private(sv.node)

        elif sv.mode == 'master':
            from ubinascii import hexlify as b2a_hex

            msg = '%d bytes:\n\n' % len(sv.raw)
            msg += str(b2a_hex(sv.raw), 'ascii')
        else:
            raise ValueError(sv.mode)

        await ux_show_story(msg, sensitive=True)

        stash.blank_object(msg)
Exemplo n.º 11
0
def generate_electrum_wallet(is_segwit):
    # Generate line-by-line JSON details about wallet.
    #
    # Much reverse enginerring of Electrum here. It's a complex
    # legacy file format.
    from main import settings

    chain = chains.current_chain()

    xfp = settings.get('xfp')

    if is_segwit:
        derive = "m/84'/{coin_type}'/{account}'".format(account=0, coin_type=chain.b44_cointype)
    else:
        derive = "m/44'/{coin_type}'/{account}'".format(account=0, coin_type=chain.b44_cointype)

    with stash.SensitiveValues() as sv:

        top = chain.serialize_public(sv.derive_path(derive))

    # most values are nicely defaulted, and for max forward compat, don't want to set
    # anything more than I need to

    rv = dict(seed_version=17, use_encryption=False, wallet_type='standard')

    # the important stuff.
    rv['keystore'] = dict(  ckcc_xfp=xfp,
                            ckcc_xpub=settings.get('xpub'),
                            hw_type='coldcard',
                            label='Coldcard Import 0x%08x' % xfp,
                            type='hardware',
                            derivation=derive, xpub=top)
        
    return rv
Exemplo n.º 12
0
def generate_unchained_export(acct_num=0):
    # They used to rely on our airgapped export file, so this is same style
    # - for multisig purposes
    # - BIP-45 style paths for now
    # - no account numbers (at this level)
    from public_constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH

    chain = chains.current_chain()
    todo = [
        ("m/45'", 'p2sh', AF_P2SH),  # iff acct_num == 0
        ("m/48'/{coin}'/{acct_num}'/1'", 'p2sh_p2wsh', AF_P2WSH_P2SH),
        ("m/48'/{coin}'/{acct_num}'/2'", 'p2wsh', AF_P2WSH),
    ]

    xfp = xfp2str(settings.get('xfp', 0))
    rv = dict(account=acct_num, xfp=xfp)

    with stash.SensitiveValues() as sv:
        for deriv, name, fmt in todo:
            if fmt == AF_P2SH and acct_num:
                continue
            dd = deriv.format(coin=chain.b44_cointype, acct_num=acct_num)
            node = sv.derive_path(dd)
            xp = chain.serialize_public(node, fmt)

            rv['%s_deriv' % name] = dd
            rv[name] = xp

    return rv
Exemplo n.º 13
0
def generate_generic_export(account_num=0):
    # Generate data that other programers will use to import Coldcard (single-signer)
    from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH

    chain = chains.current_chain()

    rv = dict(chain=chain.ctype,
                xpub = settings.get('xpub'),
                xfp = xfp2str(settings.get('xfp')),
                account = account_num,
            )

    with stash.SensitiveValues() as sv:
        # each of these paths would have /{change}/{idx} in usage (not hardened)
        for name, deriv, fmt, atype in [
            ( 'bip44', "m/44'/{ct}'/{acc}'", AF_CLASSIC, 'p2pkh' ),
            ( 'bip49', "m/49'/{ct}'/{acc}'", AF_P2WPKH_P2SH, 'p2sh-p2wpkh' ),   # was "p2wpkh-p2sh"
            ( 'bip84', "m/84'/{ct}'/{acc}'", AF_P2WPKH, 'p2wpkh' ),
        ]:
            dd = deriv.format(ct=chain.b44_cointype, acc=account_num)
            node = sv.derive_path(dd)
            xfp = xfp2str(swab32(node.my_fp()))
            xp = chain.serialize_public(node, AF_CLASSIC)
            zp = chain.serialize_public(node, fmt) if fmt != AF_CLASSIC else None

            # bonus/check: first non-change address: 0/0
            node.derive(0, False).derive(0, False)

            rv[name] = dict(deriv=dd, xpub=xp, xfp=xfp, first=chain.address(node, fmt), name=atype)
            if zp:
                rv[name]['_pub'] = zp

    return rv
Exemplo n.º 14
0
 def __init__(self, psbt_len, do_finalize=False, approved_cb=None):
     super().__init__()
     self.psbt_len = psbt_len
     self.do_finalize = do_finalize
     self.psbt = None
     self.approved_cb = approved_cb
     self.result = None      # will be (len, sha256) of the resulting PSBT
     self.chain = chains.current_chain()
Exemplo n.º 15
0
        def make_msg():
            msg = ''
            if n > 1:
                if start == 0:
                    msg = "Press 1 to save to MicroSD."
                    if version.has_fatram and not ms_wallet:
                        msg += " 4 to view QR Codes."
                    msg += '\n\n'
                msg += "Addresses %d..%d:\n\n" % (start, start + n - 1)
            else:
                # single address, from deep path given by user
                msg += "Showing single address."
                if version.has_fatram:
                    msg += " Press 4 to view QR Codes."
                msg += '\n\n'

            addrs = []
            chain = chains.current_chain()

            dis.fullscreen('Wait...')

            if ms_wallet:
                # IMPORTANT safety feature: never show complete address
                # but show enough they can verify addrs shown elsewhere.
                # - makes a redeem script
                # - converts into addr
                # - assumes 0/0 is first address.
                for (i, paths, addr, script) in ms_wallet.yield_addresses(start, n):
                    if i == 0 and ms_wallet.N <= 4:
                        msg += '\n'.join(paths) + '\n =>\n'
                    else:
                        msg += '.../0/%d =>\n' % i

                    addrs.append(addr)
                    msg += truncate_address(addr) + '\n\n'
                    dis.progress_bar_show(i/n)

            else:
                # single-singer wallets

                with stash.SensitiveValues() as sv:

                    for idx in range(start, start + n):
                        deriv = path.format(account=self.account_num, change=0, idx=idx)
                        node = sv.derive_path(deriv, register=False)
                        addr = chain.address(node, addr_fmt)
                        addrs.append(addr)

                        msg += "%s =>\n%s\n\n" % (deriv, addr)

                        dis.progress_bar_show(idx/n)

                    stash.blank_object(node)

            if n > 1:
                msg += "Press 9 to see next group, 7 to go back. X to quit."

            return msg, addrs
Exemplo n.º 16
0
async def make_multisig_menu(*a):
    # list of all multisig wallets, and high-level settings/actions
    if chains.current_chain().ctype != 'XTN':
        if not await ux_confirm(
                "Multisig is to be used on Testnet coins only at this time."):
            return

    rv = MultisigMenu.construct()
    return MultisigMenu(rv)
Exemplo n.º 17
0
async def choose_first_address(*a):
    # Choose from a truncated list of index 0 common addresses, remember
    # the last address the user selected and use it as the default
    from main import settings, dis
    chain = chains.current_chain()

    dis.fullscreen('Wait...')

    with stash.SensitiveValues() as sv:

        def truncate_address(addr):
            # Truncates address to width of screen, replacing middle chars
            middle = "-"
            leftover = SCREEN_CHAR_WIDTH - len(middle)
            start = addr[0:(leftover + 1) // 2]
            end = addr[len(addr) - (leftover // 2):]
            return start + middle + end

        # Create list of choices (address_index_0, path, addr_fmt)
        choices = []
        for name, path, addr_fmt in chains.CommonDerivations:
            if '{coin_type}' in path:
                path = path.replace('{coin_type}', str(chain.b44_cointype))
            subpath = path.format(account=0, change=0, idx=0)
            node = sv.derive_path(subpath, register=False)
            address = chain.address(node, addr_fmt)
            choices.append((truncate_address(address), path, addr_fmt))

            dis.progress_bar_show(len(choices) / len(chains.CommonDerivations))

        stash.blank_object(node)

    picked = None

    async def clicked(_1, _2, item):
        if picked is None:
            picked = item.arg
        the_ux.pop()

    items = [
        MenuItem(address, f=clicked, arg=i)
        for i, (address, path, addr_fmt) in enumerate(choices)
    ]
    menu = MenuSystem(items)
    menu.goto_idx(settings.get('axi', 0))
    the_ux.push(menu)

    await menu.interact()

    if picked is None:
        return None

    # update last clicked address
    settings.put('axi', picked)
    address, path, addr_fmt = choices[picked]

    return (path, addr_fmt)
Exemplo n.º 18
0
    def __enter__(self):
        import chains

        self.mode, self.raw, self.node = SecretStash.decode(self.secret)

        self.chain = chains.current_chain()

        self.spots = [self.secret, self.node, self.raw]

        return self
Exemplo n.º 19
0
    def __enter__(self):
        import chains

        self.mode, self.raw, self.node = SecretStash.decode(self.secret, self._bip39pw)

        self.spots.append(self.node)
        self.spots.append(self.raw)

        self.chain = chains.current_chain()

        return self
Exemplo n.º 20
0
    def import_from_psbt(cls, M, N, xpubs_list):
        # given the raw data fro PSBT global header, offer the user
        # the details, and/or bypass that all and just trust the data.
        # - xpubs_list is a list of (xfp+path, binary BIP32 xpub)
        # - already know not in our records.
        from ustruct import unpack_from
        from main import settings
        import tcc

        trust_mode = cls.get_trust_policy()

        if trust_mode == TRUST_VERIFY:
            # already checked for existing import and wasn't found, so fail
            raise AssertionError(
                "XPUBs in PSBT do not match any existing wallet")

        # build up an in-memory version of the wallet.

        assert N == len(xpubs_list)
        assert 1 <= M <= N <= MAX_SIGNERS, 'M/N range'
        my_xfp = settings.get('xfp')

        expect_chain = chains.current_chain().ctype
        xpubs = []
        has_mine = False
        path_tops = set()

        for k, v in xpubs_list:
            xfp, *path = unpack_from('<%dI' % (len(k) / 4), k, 0)
            xpub = tcc.codecs.b58_encode(v)
            xfp = cls.check_xpub(xfp, xpub, expect_chain, xpubs, path_tops)
            if xfp == my_xfp:
                has_mine = True

        assert has_mine, 'my key not included'

        name = 'PSBT-%d-of-%d' % (M, N)

        prefix = path_tops.pop() if len(path_tops) == 1 else None

        ms = cls(name, (M, N),
                 xpubs,
                 chain_type=expect_chain,
                 common_prefix=prefix)

        if trust_mode == TRUST_PSBT:
            # keep just in-memory version, no approval required
            return ms, False

        assert trust_mode == TRUST_OFFER

        # caller need to handle interact w.r.t new wallet
        print("Offering import")
        return ms, True
Exemplo n.º 21
0
async def make_bitcoin_core_wallet(account_num=0,
                                   fname_pattern='bitcoin-core.txt'):
    from glob import dis
    import ustruct
    xfp = xfp2str(settings.get('xfp'))

    dis.fullscreen('Generating...')

    # make the data
    examples = []
    imp_multi = []
    imp_desc = []
    for a, b in generate_bitcoin_core_wallet(account_num, examples):
        imp_multi.append(a)
        imp_desc.append(b)

    imp_multi = ujson.dumps(imp_multi)
    imp_desc = ujson.dumps(imp_desc)

    body = '''\
# Bitcoin Core Wallet Import File

https://github.com/Coldcard/firmware/blob/master/docs/bitcoin-core-usage.md

## For wallet with master key fingerprint: {xfp}

Wallet operates on blockchain: {nb}

## Bitcoin Core RPC

The following command can be entered after opening Window -> Console
in Bitcoin Core, or using bitcoin-cli:

importdescriptors '{imp_desc}'

### Bitcoin Core before v0.21.0 

This command can be used on older versions, but it is not as robust
and "importdescriptors" should be prefered if possible:

importmulti '{imp_multi}'

## Resulting Addresses (first 3)

'''.format(imp_multi=imp_multi,
           imp_desc=imp_desc,
           xfp=xfp,
           nb=chains.current_chain().name)

    body += '\n'.join('%s => %s' % t for t in examples)

    body += '\n'

    await write_text_file(fname_pattern, body, 'Bitcoin Core')
Exemplo n.º 22
0
 def __init__(self, psbt_len, flags=0x0, approved_cb=None, psbt_sha=None):
     super().__init__()
     self.psbt_len = psbt_len
     self.do_finalize = bool(flags & STXN_FINALIZE)
     self.do_visualize = bool(flags & STXN_VISUALIZE)
     self.stxn_flags = flags
     self.psbt = None
     self.psbt_sha = psbt_sha
     self.approved_cb = approved_cb
     self.result = None      # will be (len, sha256) of the resulting PSBT
     self.chain = chains.current_chain()
Exemplo n.º 23
0
def generate_address_csv(path, addr_fmt, n):
    # Produce CSV file contents as a generator

    yield '"Index","Payment Address","Derivation"\n'

    ch = chains.current_chain()

    with stash.SensitiveValues() as sv:
        for idx in range(n):
            subpath = path.format(account=0, change=0, idx=idx)
            node = sv.derive_path(subpath, register=False)

            yield '%d,"%s","%s"\n' % (idx, ch.address(node, addr_fmt), subpath)

        stash.blank_object(node)
Exemplo n.º 24
0
async def bitcoin_core_skeleton(*A):
    # save output descriptors into a file
    # - user has no choice, it's going to be bech32 with  m/84'/{coin_type}'/0' path
    import chains

    ch = chains.current_chain()

    if await ux_show_story('''\
This saves a command onto the MicroSD card that includes the public keys.\
You can then run that command in Bitcoin Core without ever connecting this Coldcard to a computer.\
''' + SENSITIVE_NOT_SECRET) != 'y':
        return

    # no choices to be made, just do it.
    with imported('backups') as bk:
        await bk.make_bitcoin_core_wallet()
Exemplo n.º 25
0
async def wasabi_skeleton(*A):
    # save xpub, and some other public details into a file
    # - user has no choice, it's going to be bech32 with  m/84'/0'/0' path
    import chains

    ch = chains.current_chain()

    if await ux_show_story('''\
This saves a skeleton Wasabi wallet file onto the MicroSD card. \
You can then open that file in Wasabi without ever connecting this Coldcard to a computer.\
''' + SENSITIVE_NOT_SECRET) != 'y':
        return

    # no choices to be made, just do it.
    with imported('backups') as bk:
        await bk.make_json_wallet('Wasabi wallet', lambda: bk.generate_wasabi_wallet(), 'new-wasabi.json')
Exemplo n.º 26
0
    async def render(self):
        # Choose from a truncated list of index 0 common addresses, remember
        # the last address the user selected and use it as the default
        from glob import dis
        chain = chains.current_chain()

        dis.fullscreen('Wait...')

        with stash.SensitiveValues() as sv:

            # Create list of choices (address_index_0, path, addr_fmt)
            choices = []
            for name, path, addr_fmt in chains.CommonDerivations:
                if '{coin_type}' in path:
                    path = path.replace('{coin_type}', str(chain.b44_cointype))

                if self.account_num != 0 and '{account}' not in path:
                    # skip derivations that are not affected by account number
                    continue

                deriv = path.format(account=self.account_num, change=0, idx=0)
                node = sv.derive_path(deriv, register=False)
                address = chain.address(node, addr_fmt)
                choices.append( (truncate_address(address), path, addr_fmt) )

                dis.progress_bar_show(len(choices) / len(chains.CommonDerivations))

            stash.blank_object(node)

        items = [MenuItem(address, f=self.pick_single, arg=(path, addr_fmt)) 
                        for i, (address, path, addr_fmt) in enumerate(choices)]

        # some other choices
        if self.account_num == 0:
            items.append(MenuItem("Account Number", f=self.change_account))
            items.append(MenuItem("Custom Path", menu=self.make_custom))

            # if they have MS wallets, add those next
            for ms in MultisigWallet.iter_wallets():
                if not ms.addr_fmt: continue
                items.append(MenuItem(ms.name, f=self.pick_multisig, arg=ms))
        else:
            items.append(MenuItem("Account: %d" % self.account_num, f=self.change_account))

        self.goto_idx(settings.get('axi', 0))      # weak

        self.replace_items(items)
Exemplo n.º 27
0
def generate_electrum_wallet(addr_type, account_num=0):
    # Generate line-by-line JSON details about wallet.
    #
    # Much reverse enginerring of Electrum here. It's a complex
    # legacy file format.
    from main import settings
    from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH

    chain = chains.current_chain()

    xfp = settings.get('xfp')

    # Must get the derivation path, and the SLIP32 version bytes right!
    if addr_type == AF_CLASSIC:
        mode = 44
    elif addr_type == AF_P2WPKH:
        mode = 84
    elif addr_type == AF_P2WPKH_P2SH:
        mode = 49
    else:
        raise ValueError(addr_type)

    derive = "m/{mode}'/{coin_type}'/{account}'".format(
        mode=mode, account=account_num, coin_type=chain.b44_cointype)

    with stash.SensitiveValues() as sv:
        top = chain.serialize_public(sv.derive_path(derive), addr_type)

    # most values are nicely defaulted, and for max forward compat, don't want to set
    # anything more than I need to

    rv = dict(seed_version=17, use_encryption=False, wallet_type='standard')

    lab = 'Coldcard Import %s' % xfp2str(xfp)
    if account_num:
        lab += ' Acct#%d' % account_num

    # the important stuff.
    rv['keystore'] = dict(ckcc_xfp=xfp,
                          ckcc_xpub=settings.get('xpub'),
                          hw_type='coldcard',
                          type='hardware',
                          label=lab,
                          derivation=derive,
                          xpub=top)

    return rv
Exemplo n.º 28
0
    def verify_amount(cls, prevout, amount, in_idx):
        # check this input either:
        #   - not been seen before, in which case, record it
        #   - OR: the amount matches exactly, any previously-seend UTXO w/ same outpoint
        # raises IncorrectUTXOAmount with details if it fails, which should abort any signing
        exp = cls.fetch_amount(prevout)

        if exp is None:
            # new entry, add it
            cls.add(prevout, amount)

        elif exp != amount:
            # Found the hacking we are looking for!
            ch = chains.current_chain()
            exp, units = ch.render_value(exp, True)
            amount, _ = ch.render_value(amount, True)

            raise IncorrectUTXOAmount(in_idx, "Expected %s but PSBT claims %s %s" % (
                                                exp, amount, units))
Exemplo n.º 29
0
    def handle_xpub(self, subpath):
        # Share the xpub for the indicated subpath. Expects
        # a text string which is the path derivation.

        # TODO: might not have a privkey yet

        from chains import current_chain
        from utils import cleanup_deriv_path

        subpath = cleanup_deriv_path(subpath)

        chain = current_chain()

        with stash.SensitiveValues() as sv:
            node = sv.derive_path(subpath)

            xpub = chain.serialize_public(node)

            return b'asci' + xpub.encode()
Exemplo n.º 30
0
    def to_text(self):
        # Text for humans to read and approve.
        chain = chains.current_chain()

        def render(n):
            return ' '.join(chain.render_value(n, True))

        if self.per_period is not None:
            rv = 'Up to %s per period' % render(self.per_period)
            if self.max_amount is not None:
                rv += ', and up to %s per txn' % render(self.max_amount)
        elif self.max_amount is not None:
            rv = 'Up to %s per txn' % render(self.max_amount)
        else:
            rv = 'Any amount'

        if self.wallet == '1':
            rv += ' (non multisig)'
        elif self.wallet:
            rv += ' from multisig wallet "%s"' % self.wallet

        if self.users:
            rv += ' may be authorized by '
            if self.min_users == len(self.users) == 1:
                rv += 'user: '******'all users: ' + ', '.join(self.users)
            elif self.min_users == 1:
                rv += 'any one user: '******' OR '.join(self.users)
            elif self.min_users:
                rv += 'at least %d users: ' % self.min_users
                rv += ', '.join(self.users)
        else:
            rv += ' will be approved'

        if self.whitelist:
            rv += ' provided it goes to: ' + ', '.join(self.whitelist)

        if self.local_conf:
            rv += ' if local user confirms'

        return rv