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))
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
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
def current_chain(): # return chain matching current setting from common import settings chain = settings.get('chain', 'BTC') return get_chain(chain)
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)
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
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)
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
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)
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)
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)
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)
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")
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 }
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
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)
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
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
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
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')
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)
def on_thresh_selection_combo_changed(self, widget): self.set_thresholds(settings.get('camera/thresholds/%s' % self.get_selected_threshold_setting()))
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)
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)
def exists(cls): # are there any wallets defined? from common import settings return bool(settings.get('multisig', False))
#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),