Пример #1
0
    async def interact(self):
        # prompt them
        from common import settings

        showit = False
        while 1:
            if showit:
                ch = await ux_show_story('''Given:\n\n%s\n\nShould we switch to that wallet now?

OK to continue, X to cancel.''' % self._pw, title="Passphrase")
            else:
                ch = await ux_show_story('''BIP39 passphrase (%d chars long) has been provided over USB connection. Should we switch to that wallet now?

Press 2 to view the provided passphrase.\n\nOK to continue, X to cancel.''' % len(self._pw), title="Passphrase")

            if ch == '2':
                showit = True
                continue
            break

        try:
            if ch != 'y':
                # User chose not to sign the transaction
                self.refused = True

                # TODO: ux_confirm() instead?
                # await ux_dramatic_pause("Refused.", 1)
            else:
                from seed import set_bip39_passphrase

                # full screen message shown: "Working..."
                err = set_bip39_passphrase(self._pw)

                if err:
                    await self.failure(err)
                else:
                    self.result = settings.get('xpub')

        except BaseException as exc:
            self.failed = "Exception"
            sys.print_exception(exc)
        finally:
            self.done()

        if self.result:
            new_xfp = settings.get('xfp')
            await ux_show_story('''Above is the master key fingerprint of the current wallet.''',
                                title="[%s]" % xfp2str(new_xfp))
Пример #2
0
    def commit(self):
        # data to save
        # - important that this fails immediately when nvram overflows
        from common import settings

        obj = self.serialize()

        v = settings.get('multisig', [])
        orig = v.copy()
        if not v or self.storage_idx == -1:
            # create
            self.storage_idx = len(v)
            v.append(obj)
        else:
            # update: no provision for changing fingerprints
            assert sorted(k for k, v in v[self.storage_idx][2]) == self.xfps
            v[self.storage_idx] = obj

        settings.set('multisig', v)

        # save now, rather than in background, so we can recover
        # from out-of-space situation
        try:
            settings.save()
        except:
            # back out change; no longer sure of NVRAM state
            try:
                settings.set('multisig', orig)
                settings.save()
            except:
                pass        # give up on recovery

            raise MultisigOutOfSpace
Пример #3
0
    async def export_wallet_file(self, mode="exported from", extra_msg=None):
        # create a text file with the details; ready for import to next Passport
        from common import settings
        my_xfp = xfp2str(settings.get('xfp'))

        fname_pattern = self.make_fname('export')

        try:
            with CardSlot() as card:
                fname, nice = card.pick_filename(fname_pattern)

                # do actual write
                with open(fname, 'wt') as fp:
                    print("# Passport Multisig setup file (%s %s)\n#" %
                          (mode, my_xfp), file=fp)
                    self.render_export(fp)

            msg = '''Passport multisig setup file written:\n\n%s''' % nice
            if extra_msg:
                msg += extra_msg

            await ux_show_story(msg)

        except CardMissingError:
            await needs_microsd()
            return
        except Exception as e:
            await ux_show_story('Failed to write!\n\n\n'+str(e))
            return
Пример #4
0
def current_chain():
    # return chain matching current setting
    from common import settings

    chain = settings.get('chain', 'BTC')

    return get_chain(chain)
Пример #5
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)
Пример #6
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 common 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 = 'Passport 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='passport',
                          type='hardware',
                          label=lab,
                          derivation=derive,
                          xpub=top)

    return rv
Пример #7
0
    def get_all(cls):
        # return them all, as a generator
        from common import settings

        lst = settings.get('multisig', [])

        for idx, v in enumerate(lst):
            yield cls.deserialize(v, idx)
Пример #8
0
    def get_trust_policy(cls):
        from common import settings

        which = settings.get('pms', None)

        if which is None:
            which = TRUST_VERIFY if cls.exists() else TRUST_OFFER

        return which
Пример #9
0
    def __preload_chooser(self):
        choices = settings.get('autonomous/choices')
        if choices is not None:

            model = self.auto_chooser.get_model()
            model.clear()

            for choice in choices:
                model.append((choice, ))
 def __preload_chooser(self):
     choices = settings.get('autonomous/choices')
     if choices is not None:
         
         model = self.auto_chooser.get_model()
         model.clear()
         
         for choice in choices:
             model.append((choice,))
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 common import settings, dis
    chain = chains.current_chain()

    dis.fullscreen('Loading...')

    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, title='Address List')
    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.set('axi', picked)
    address, path, addr_fmt = choices[picked]

    return (path, addr_fmt)
Пример #12
0
    def initialize(self):
        
        # store references to these so we don't have to do it later
        self.thresh_widgets = [getattr(self, 'adj_%s' % name) for name in self.thresh_names]

        # always setup builtins, they shouldn't change at all
        default_settings = None
        
        for name, setting, is_default in self.settings:
            if is_default:
                default_settings = setting
            settings.set('camera/thresholds/%s' % name, setting)
        
        
        
        # setup widgets with thresholds
        # -> this implicitly sets up the detector correctly, since the widget
        #    change event changes the detector value
        current_thresholds = [settings.get('camera/%s' % name, default) for name, default in izip(self.thresh_names, default_settings)]
        current_thresholds.append(settings.get('camera/colorspace', 'HSV'))
        self.set_thresholds(current_thresholds)
        
        
        # initialize the combo box with existing settings
        # -> but block the handler, so we don't accidentally set the settings again
        self.thresh_selection_combo.handler_block_by_func(self.on_thresh_selection_combo_changed)
        
        for i, (name, value) in enumerate(settings.items('camera/thresholds')):
            
            self.thresh_model.append((name,))
            
            # if the value matches completely, set this as the currently selected setting
            # -> this way, the user knows the current settings aren't saved!
            match = True
            for tvalue, cvalue in izip(value, current_thresholds[:6]):
                if int(tvalue) != int(cvalue):
                    match = False
                    break
                
            if match:
                self.thresh_selection_combo.set_active(i)
        
        self.thresh_selection_combo.handler_unblock_by_func(self.on_thresh_selection_combo_changed)
Пример #13
0
    def initialize(self):
        
        # store references to these so we don't have to do it later
        self.thresh_widgets = [getattr(self, 'adj_%s' % name) for name in self.thresh_names]

        # always setup builtins, they shouldn't change at all
        settings.set('camera/thresholds/Competition', self.kCompSettings)
        settings.set('camera/thresholds/Pit', self.kPitSettings)
        
        # setup widgets with thresholds
        # -> this implicitly sets up the detector correctly, since the widget
        #    change event changes the detector value
        current_thresholds = [settings.get('camera/%s' % name, default) for name, default in izip(self.thresh_names, self.kCompSettings)]
        self.set_thresholds(current_thresholds)
        
        
        # initialize the combo box with existing settings
        # -> but block the handler, so we don't accidentally set the settings again
        self.thresh_selection_combo.handler_block_by_func(self.on_thresh_selection_combo_changed)
        
        for i, (name, value) in enumerate(settings.items('camera/thresholds')):
            
            self.thresh_model.append((name,))
            
            # if the value matches completely, set this as the currently selected setting
            # -> this way, the user knows the current settings aren't saved!
            match = True
            for tvalue, cvalue in izip(value, current_thresholds):
                if int(tvalue) != int(cvalue):
                    match = False
                    break
                
            if match:
                self.thresh_selection_combo.set_active(i)
        
        self.thresh_selection_combo.handler_unblock_by_func(self.on_thresh_selection_combo_changed)
        
        # initialize aim height variable
        aim_horizontal = settings.get('targeting/aim_horizontal', target_data.kOptimumHorizontalPosition)
        self.adj_aim_horizontal.set_value(aim_horizontal * 100.0)
        
        aim_vertical = settings.get('targeting/aim_vertical', target_data.kOptimumVerticalPosition)
        self.adj_aim_vertical.set_value(aim_vertical * 100.0)
Пример #14
0
    def get_by_idx(cls, nth):
        # instance from index number
        from common import settings
        lst = settings.get('multisig', [])
        try:
            obj = lst[nth]
        except IndexError:
            return None

        return cls.deserialize(obj, nth)
Пример #15
0
 def _static_processing(self):
     
     logger.info("Static processing thread starting")
     idx = -1
     
     # resume processing with the last image the user looked at
     last_img = settings.get('processing/last_img', None)
     for i, image_name in enumerate(self.images):
         if image_name == last_img:
             self.idx = i
             break 
     
     while True:
     
         with self.condition:
             
             # wait until the user hits a key
             while idx == self.idx and not self.do_stop and not self.do_refresh:
                 self.condition.wait()
             
             if self.do_stop:
                 break
             
             idx = self.idx
             self.do_refresh = False
                 
         # if the index is valid, then process an image
         if idx < len(self.images) and idx >= 0:
             
             image_name = self.images[idx]
             
             logger.info("Opening %s" % image_name)
             
             img = cv2.imread(image_name)
             if img is None:
                 logger.error("Error opening %s: could not read file" % (image_name))
                 self.idx += self.idx_increment
                 continue
             
             try:
                 target_data = self.detector.processImage(img)
             except:
                 logutil.log_exception(logger, 'error processing image')
             else:
                 settings.set('processing/last_img', image_name)
                 settings.save()
                 
                 logger.info('Finished processing')
             
                 # note that you cannot typically interact with the UI
                 # from another thread -- but this function is special
                 self.camera_widget.set_target_data(target_data)
         
     logger.info("Static processing thread exiting")
Пример #16
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 common 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
        }
Пример #17
0
    def delete(self):
        # remove saved entry
        # - important: not expecting more than one instance of this class in memory
        from common import settings

        assert self.storage_idx >= 0

        # safety check
        expect_idx = self.find_match(self.M, self.N, self.xfps)
        assert expect_idx == self.storage_idx

        lst = settings.get('multisig', [])
        del lst[self.storage_idx]
        settings.set('multisig', lst)
        settings.save()

        self.storage_idx = -1
Пример #18
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 common 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 = ustruct.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)

        # may just keep just in-memory version, no approval required, if we are
        # trusting PSBT's today, otherwise caller will need to handle UX w.r.t new wallet
        return ms, (trust_mode != TRUST_PSBT)
Пример #19
0
    def find_match(cls, M, N, fingerprints):
        # Find index of matching wallet. Don't de-serialize everything.
        # - returns index, or -1 if not found
        # - fingerprints are iterable of uint32's: may not be unique!
        # - M, N must be known.
        from common import settings
        lst = settings.get('multisig', [])

        fingerprints = sorted(fingerprints)
        assert N == len(fingerprints)

        for idx, rec in enumerate(lst):
            name, m_of_n, xpubs, opts = rec
            if tuple(m_of_n) != (M, N):
                continue
            if sorted(f for f, _ in xpubs) != fingerprints:
                continue
            return idx

        return -1
Пример #20
0
    def find_candidates(cls, fingerprints):
        # Find index of matching wallet and M value. Don't de-serialize everything.
        # - returns set of matches, each with M value
        # - fingerprints are iterable of uint32's
        from common import settings
        lst = settings.get('multisig', [])

        fingerprints = sorted(fingerprints)
        N = len(fingerprints)
        rv = []

        for idx, rec in enumerate(lst):
            name, m_of_n, xpubs, opts = rec
            if m_of_n[1] != N:
                continue
            if sorted(f for f, _ in xpubs) != fingerprints:
                continue

            rv.append(idx)

        return rv
Пример #21
0
    def quick_check(cls, M, N, xfp_xor):
        # quicker USB method.
        from common import settings
        lst = settings.get('multisig', [])

        rv = []
        for rec in lst:
            name, m_of_n, xpubs, opts = rec
            if m_of_n[0] != M:
                continue
            if m_of_n[1] != N:
                continue

            x = 0
            for xfp, _ in xpubs:
                x ^= xfp
            if x != xfp_xor:
                continue

            return True

        return False
Пример #22
0
async def make_bitcoin_core_wallet(account_num=0,
                                   fname_pattern='bitcoin-core.txt'):
    from common import dis, settings
    import ustruct
    xfp = xfp2str(settings.get('xfp'))

    dis.fullscreen('Generating...')

    # make the data
    examples = []
    payload = ujson.dumps(
        list(generate_bitcoin_core_wallet(examples, account_num)))

    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:

importmulti '{payload}'

## Resulting Addresses (first 3)

'''.format(payload=payload, 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')
Пример #23
0
async def export_multisig_xpubs(*a):
    # WAS: Create a single text file with lots of docs, and all possible useful xpub values.
    # THEN: Just create the one-liner xpub export value they need/want to support BIP45
    # NOW: Export JSON with one xpub per useful address type and semi-standard derivation path
    #
    # Consumer for this file is supposed to be ourselves, when we build on-device multisig.
    #
    from common import settings
    xfp = xfp2str(settings.get('xfp', 0))
    chain = chains.current_chain()

    fname_pattern = 'ccxp-%s.json' % xfp

    msg = '''\
This feature creates a small file containing \
the extended public keys (XPUB) you would need to join \
a multisig wallet using the 'Create Airgapped' feature.

The public keys exported are:

BIP45:
   m/45'
P2WSH-P2SH:
   m/48'/{coin}'/0'/1'
P2WSH:
   m/48'/{coin}'/0'/2'

OK to continue. X to abort.
'''.format(coin=chain.b44_cointype)

    resp = await ux_show_story(msg)
    if resp != 'y':
        return

    try:
        with CardSlot() as card:
            fname, nice = card.pick_filename(fname_pattern)
            # do actual write: manual JSON here so more human-readable.
            with open(fname, 'wt') as fp:
                fp.write('{\n')
                with stash.SensitiveValues() as sv:
                    for deriv, name, fmt in [
                        ("m/45'", 'p2sh', AF_P2SH),
                        ("m/48'/{coin}'/0'/1'", 'p2wsh_p2sh', AF_P2WSH_P2SH),
                        ("m/48'/{coin}'/0'/2'", 'p2wsh', AF_P2WSH)
                    ]:

                        dd = deriv.format(coin=chain.b44_cointype)
                        node = sv.derive_path(dd)
                        xp = chain.serialize_public(node, fmt)
                        fp.write('  "%s_deriv": "%s",\n' % (name, dd))
                        fp.write('  "%s": "%s",\n' % (name, xp))

                fp.write('  "xfp": "%s"\n}\n' % xfp)

    except CardMissingError:
        await needs_microsd()
        return
    except Exception as e:
        await ux_show_story('Failed to write!\n\n\n'+str(e))
        return

    msg = '''BIP45 multisig xpub file written:\n\n%s''' % nice
    await ux_show_story(msg)
Пример #24
0
 def on_thresh_selection_combo_changed(self, widget):
     self.set_thresholds(settings.get('camera/thresholds/%s' % self.get_selected_threshold_setting()))
Пример #25
0
    def from_file(cls, config, name=None):
        # Given a simple text file, parse contents and create instance (unsaved).
        # format is:         label: value
        # where label is:
        #       name: nameforwallet
        #       policy: M of N
        #       (8digithex): xpub of cosigner
        #
        # quick checks:
        # - name: 1-20 ascii chars
        # - M of N line (assume N of N if not spec'd)
        # - xpub: any bip32 serialization we understand, but be consistent
        #
        from common import settings

        my_xfp = settings.get('xfp')
        common_prefix = None
        xpubs = []
        path_tops = set()
        M, N = -1, -1
        has_mine = False
        addr_fmt = AF_P2SH
        expect_chain = chains.current_chain().ctype

        lines = config.split('\n')

        for ln in lines:
            # remove comments
            comm = ln.find('#')
            if comm != -1:
                ln = ln[0:comm]

            ln = ln.strip()

            if ':' not in ln:
                if 'pub' in ln:
                    # optimization: allow bare xpub if we can calc xfp
                    label = '0'*8
                    value = ln
                else:
                    # complain?
                    if ln:
                        print("no colon: " + ln)
                    continue
            else:
                label, value = ln.split(':')
                label = label.lower()

            value = value.strip()

            if label == 'name':
                name = value
            elif label == 'policy':
                try:
                    # accepts: 2 of 3    2/3    2,3    2 3   etc
                    mat = ure.search(r'(\d+)\D*(\d+)', value)
                    assert mat
                    M = int(mat.group(1))
                    N = int(mat.group(2))
                    assert 1 <= M <= N <= MAX_SIGNERS
                except:
                    raise AssertionError('bad policy line')

            elif label == 'derivation':
                # reveal the **common** path derivation for all keys
                try:
                    cp = cleanup_deriv_path(value)
                    # - not storing "m/" prefix, nor 'm' case which doesn't add any info
                    common_prefix = None if cp == 'm' else cp[2:]
                except BaseException as exc:
                    raise AssertionError('bad derivation line: ' + str(exc))

            elif label == 'format':
                # pick segwit vs. classic vs. wrapped version
                value = value.lower()
                for fmt_code, fmt_label in cls.FORMAT_NAMES:
                    if value == fmt_label:
                        addr_fmt = fmt_code
                        break
                else:
                    raise AssertionError('bad format line')
            elif len(label) == 8:
                try:
                    xfp = str2xfp(label)
                except:
                    # complain?
                    #print("Bad xfp: " + ln)
                    continue

                # deserialize, update list and lots of checks
                xfp = cls.check_xpub(
                    xfp, value, expect_chain, xpubs, path_tops)

                if xfp == my_xfp:
                    # not conclusive, but enough for error catching.
                    has_mine = True

        assert len(xpubs), 'need xpubs'

        if M == N == -1:
            # default policy: all keys
            N = M = len(xpubs)

        if not name:
            # provide a default name
            name = '%d-of-%d' % (M, N)

        try:
            name = str(name, 'ascii')
            assert 1 <= len(name) <= 20
        except:
            raise AssertionError('name must be ascii, 1..20 long')

        assert 1 <= M <= N <= MAX_SIGNERS, 'M/N range'
        assert N == len(xpubs), 'wrong # of xpubs, expect %d' % N
        assert addr_fmt & AFC_SCRIPT, 'script style addr fmt'

        # check we're included... do not insert ourselves, even tho we
        # have enough info, simply because other signers need to know my xpubkey anyway
        assert has_mine, 'my key not included'

        if not common_prefix and len(path_tops) == 1:
            # fill in the common prefix iff we can deduce it from xpubs
            common_prefix = path_tops.pop()

        # done. have all the parts
        return cls(name, (M, N), xpubs, addr_fmt=addr_fmt,
                   chain_type=expect_chain, common_prefix=common_prefix)
Пример #26
0
async def ondevice_multisig_create(mode='p2wsh', addr_fmt=AF_P2WSH):
    # collect all xpub- exports on current SD card (must be > 1)
    # - ask for M value
    # - create wallet, save and also export
    # - also create electrum skel to go with that
    # - only expected to work with our ccxp-foo.json export files.
    from actions import file_picker
    import uos
    import ujson
    from utils import get_filesize
    from common import settings

    chain = chains.current_chain()
    my_xfp = settings.get('xfp')

    xpubs = []
    files = []
    has_mine = False
    deriv = None
    try:
        with CardSlot() as card:
            for path in card.get_paths():
                for fn, ftype, *var in uos.ilistdir(path):
                    if ftype == 0x4000:
                        # ignore subdirs
                        continue

                    if not fn.startswith('ccxp-') or not fn.endswith('.json'):
                        # wrong prefix/suffix: ignore
                        continue

                    full_fname = path + '/' + fn

                    # Conside file size
                    # sigh, OS/filesystem variations
                    file_size = var[1] if len(
                        var) == 2 else get_filesize(full_fname)

                    if not (0 <= file_size <= 1000):
                        # out of range size
                        continue

                    try:
                        with open(full_fname, 'rt') as fp:
                            vals = ujson.load(fp)

                        ln = vals.get(mode)

                        # value in file is BE32, but we want LE32 internally
                        xfp = str2xfp(vals['xfp'])
                        if not deriv:
                            deriv = vals[mode+'_deriv']
                        else:
                            assert deriv == vals[mode +
                                                 '_deriv'], "wrong derivation"

                        node, _, _ = import_xpub(ln)

                        if xfp == my_xfp:
                            has_mine = True

                        xpubs.append(
                            (xfp, chain.serialize_public(node, AF_P2SH)))
                        files.append(fn)

                    except CardMissingError:
                        raise

                    except Exception as exc:
                        # show something for coders, but no user feedback
                        sys.print_exception(exc)
                        continue

    except CardMissingError:
        await needs_microsd()
        return

    # remove dups; easy to happen if you double-tap the export
    delme = set()
    for i in range(len(xpubs)):
        for j in range(len(xpubs)):
            if j in delme:
                continue
            if i == j:
                continue
            if xpubs[i] == xpubs[j]:
                delme.add(j)
    if delme:
        xpubs = [x for idx, x in enumerate(xpubs) if idx not in delme]

    if not xpubs or len(xpubs) == 1 and has_mine:
        await ux_show_story("Unable to find any Passport exported keys on this card. Must have filename: ccxp-....json")
        return

    # add myself if not included already
    if not has_mine:
        with stash.SensitiveValues() as sv:
            node = sv.derive_path(deriv)
            xpubs.append((my_xfp, chain.serialize_public(node, AF_P2SH)))

    N = len(xpubs)

    if N > MAX_SIGNERS:
        await ux_show_story("Too many signers, max is %d." % MAX_SIGNERS)
        return

    # pick useful M value to start
    assert N >= 2
    M = (N - 1) if N < 4 else ((N//2)+1)

    while 1:
        msg = '''How many need to sign?\n      %d of %d

Press (7 or 9) to change M value, or OK \
to continue.

If you expected more or less keys (N=%d #files=%d), \
then check card and file contents.

Passport multisig setup file and an Electrum wallet file will be created automatically.\
''' % (M, N, N, len(files))

        # TODO: Update key handling
        ch = await ux_show_story(msg, escape='123479')

        if ch in '1234':
            M = min(N, int(ch))     # undocumented shortcut
        elif ch == 'x':
            await ux_dramatic_pause('Aborted.', 2)
            return
        elif ch == 'y':
            break

    # create appropriate object
    assert 1 <= M <= N <= MAX_SIGNERS

    name = 'CC-%d-of-%d' % (M, N)
    ms = MultisigWallet(name, (M, N), xpubs, chain_type=chain.ctype,
                        common_prefix=deriv[2:], addr_fmt=addr_fmt)

    from auth import NewEnrollRequest, UserAuthorizedAction

    UserAuthorizedAction.active_request = NewEnrollRequest(
        ms, auto_export=True)

    # menu item case: add to stack
    from ux import the_ux
    the_ux.push(UserAuthorizedAction.active_request)
Пример #27
0
 def exists(cls):
     # are there any wallets defined?
     from common import settings
     return bool(settings.get('multisig', False))
Пример #28
0
 def on_thresh_selection_combo_changed(self, widget):
     self.set_thresholds(settings.get('camera/thresholds/%s' % self.get_selected_threshold_setting()))
Пример #29
0
    #MenuItem("Backup System", f=backup_everything),
    MenuItem("Dump Summary", f=dump_summary),
    MenuItem('Export Wallet', menu=ExportWalletMenu),
    #MenuItem('Sign Text File', predicate=has_secrets, f=sign_message_on_sd),
    #MenuItem('Upgrade From SD', f=microsd_upgrade),
    MenuItem('Format Card', f=wipe_sd_card),
    MenuItem('List Files', f=list_files),
]

AdvancedMenu = [
    MenuItem('Change PIN', f=change_pin),
    MenuItem("MicroSD Settings", menu=SDCardMenu),
    MenuItem("List Addresses", f=coming_soon),
    MenuItem('View Seed Words',
             f=view_seed_words,
             predicate=lambda: settings.get('words', True)),
    MenuItem("Erase Wallet", f=clear_seed),

    # TODO: Don't we want to allow for this?
    # MenuItem('Lock Down Seed', f=convert_bip39_to_bip32,
    #          predicate=lambda: settings.get('words', True)),
]

BackupMenu = [
    MenuItem("Create Backup", menu=coming_soon),  #f=backup_everything),
    MenuItem("Verify Backup", menu=coming_soon),  #f=verify_backup),
    MenuItem("Restore Backup", menu=coming_soon),  #f=restore_everything),
]

SettingsMenu = [
    MenuItem("About", f=view_ident),