From ad9d1774a5cf7bfe1755dc79a65daf6c9be805e3 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Apr 2024 21:19:31 -0400 Subject: [PATCH 01/28] SFT-1728: starting with temporary settings mode --- ports/stm32/boards/Passport/modules/export.py | 7 +- .../boards/Passport/modules/ext_settings.py | 94 ++++++++++++++++--- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/export.py b/ports/stm32/boards/Passport/modules/export.py index 8f7e866ed..0e14ed7ec 100644 --- a/ports/stm32/boards/Passport/modules/export.py +++ b/ports/stm32/boards/Passport/modules/export.py @@ -86,7 +86,12 @@ def ADD(key, val): # user preferences - sort so that accounts is processed before multisig multisig_ids = [] - for k, v in sorted(settings.current.items()): + settings_dict = None + if settings.temporary_mode: + settings_dict = settings.temporary_settings + else: + settings_dict = settings.current + for k, v in sorted(settings_dict.items()): # print('render handling key "{}"'.format(k)) if k[0] == '_': continue # debug stuff in simulator diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 884293cae..7972afc63 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -21,6 +21,7 @@ import trezorcrypto import ustruct import gc +import ucopy from uio import BytesIO from sffile import SFFile from utils import to_str, call_later_ms @@ -45,6 +46,9 @@ def __init__(self, slots=None, slot_size=0, loop=None, name=None): self.current = self.default_values() self.overrides = {} # volatile overide values self.last_save_slots = [-1, -1] + self.temporary_mode = False + self.temporary_settings = {} + self.temporary_overrides = {} # NOTE: We don't load the settings initially since we don't have the AES key until # the user logs in successfully. @@ -206,13 +210,13 @@ def load(self): sf.write(pos + i, h) def get(self, kn, default=None): - if kn in self.overrides: - return self.overrides.get(kn) + if self.in_overrides(kn): + return self.get_from_overrides(kn) else: # Special case for xfp and xpub -- make sure they exist and create if not - if kn not in self.current: - if kn == 'root_xfp' and 'xfp' in self.current: - return self.current.get('xfp', default) + if not self.in_current(kn): + if kn == 'root_xfp' and self.in_current('xfp'): + return self.get_from_current('xfp', default) if kn == 'xfp' or kn == 'xpub' or kn == 'root_xfp': try: # Update xpub/xfp in settings after creating new wallet @@ -229,9 +233,9 @@ def get(self, kn, default=None): finally: # system.hide_busy_bar() # These are overrides, so return them from there - return self.overrides.get(kn) + return self.get_from_overrides(kn) - return self.current.get(kn, default) + return self.get_from_current(kn, default) def changed(self): self.is_dirty += 1 @@ -240,26 +244,64 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) + if self.temporary_mode: + self.temporary_settings[kn] = v + return + self.current[kn] = v self.changed() + def get_from_current(kn, default): + if self.temporary_mode: + return self.temporary_settings.get(kn, default) + return self.current.get(kn, default) + + def get_from_overrides(kn, default): + if self.temporary_mode: + return self.temporary_overrides.get(kn, default) + return self.overrides.get(kn, default) + def set_volatile(self, kn, v): + if self.temporary_mode: + self.temporary_overrides[kn] = v + return self.overrides[kn] = v + def in_current(self, kn): + if self.temporary_mode: + return kn in self.temporary_settings + return kn in self.current + + def in_overrides(self, kn): + if self.temporary_mode: + return kn in self.temporary_overrides + return kn in self.overrides + def clear_volatile(self, kn): + if self.temporary_mode: + if kn in self.temporary_overrides: + del self.temporary_overrides[kn] + return if kn in self.overrides: del self.overrides[kn] def remove(self, kn): # print('remove(\'{}\') called!'.format(kn)) - if kn in self.current: + if self.in_current(kn): + if self.temporary_mode: + self.temporary_settings.pop(kn, None) + return self.current.pop(kn, None) self.changed() def remove_regex(self, pattern): import re pattern = re.compile(pattern) - matches = [k for k in self.current if pattern.search(k)] + matches = [] + if self.temporary_mode: + matches = [k for k in self.temporary_settings if pattern.search(k)] + else: + matches = [k for k in self.current if pattern.search(k)] for k in matches: self.remove(k) @@ -268,9 +310,14 @@ def clear(self): # could be just: # self.current = {} # but accomodating the simulator here - rk = [k for k in self.current if k[0] != '_'] - for k in rk: - del self.current[k] + if self.temporary_mode: + rk = [k for k in self.temporary_settings if k[0] != '_'] + for k in rk: + del self.temporary_settings[k] + else: + rk = [k for k in self.current if k[0] != '_'] + for k in rk: + del self.current[k] self.changed() @@ -338,11 +385,17 @@ def erase_cache_entry(self, start_pos): sf.wait_done() def erase_all(self): + if self.temporary_mode: + return + for pos in self.slots: self.erase_cache_entry(pos) self.blank() def save(self): + if self.temporary_mode: + return + # Make two saves in case one is corrupted self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) @@ -426,6 +479,23 @@ def blank(self): self.current.clear() self.is_dirty = 0 + def enter_temporary_mode(self): + + # Avoid resetting temporary settings if already in temporary mode + if self.temporary_mode: + return + + self.temporary_mode = True + + # Merge the overrides and persistent settings into a new dict + self.temporary_settings = copy.deepcopy(self.current) + self.temporary_overrides = copy.deepcopy(self.overrides) + + def exit_temporary_mode(self): + self.temporary_mode = False + self.temporary_settings = {} + self.temporary_overrides = {} + @staticmethod def default_values(): # Please try to avoid defaults here... It's better to put into code From 8ac3e31766d8e4f5bf01caf4dee8f010c65723f8 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Apr 2024 21:58:40 -0400 Subject: [PATCH 02/28] SFT-1728: fixed copying and added device settings exception --- ports/stm32/boards/Passport/modules/ext_settings.py | 10 +++++----- .../stm32/boards/Passport/modules/public_constants.py | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 7972afc63..707d9c476 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -21,12 +21,12 @@ import trezorcrypto import ustruct import gc -import ucopy from uio import BytesIO from sffile import SFFile from utils import to_str, call_later_ms from constants import SPI_FLASH_SECTOR_SIZE from passport import mem +from public_constants import DEVICE_SETTINGS class ExtSettings: @@ -244,14 +244,14 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode: + if self.temporary_mode and kn not in DEVICE_SETTINGS: self.temporary_settings[kn] = v return self.current[kn] = v self.changed() - def get_from_current(kn, default): + def get_from_current(self, kn, default): if self.temporary_mode: return self.temporary_settings.get(kn, default) return self.current.get(kn, default) @@ -488,8 +488,8 @@ def enter_temporary_mode(self): self.temporary_mode = True # Merge the overrides and persistent settings into a new dict - self.temporary_settings = copy.deepcopy(self.current) - self.temporary_overrides = copy.deepcopy(self.overrides) + self.temporary_settings.update(self.current) + self.temporary_overrides.update(self.overrides) def exit_temporary_mode(self): self.temporary_mode = False diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 5d4ea026e..15dd7e4d0 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -123,6 +123,13 @@ MARGIN_FOR_ADDRESSES = 0 +# Device Settings, not saved to backups in temporary mode +DEVICE_SETTINGS = [ + 'screen_brightness', + 'shutdown_timeout', + 'device_name' +] + # Size of a pin prefix: NUM_DIGITS_FOR_SECURITY_WORDS = const(4) From a832451897fe4a6fee348630bd31ef3867f4c6c7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 5 Apr 2024 13:57:00 -0400 Subject: [PATCH 03/28] SFT-1728: importing temporary seed working --- .../boards/Passport/modules/ext_settings.py | 23 +++++++++------- .../modules/flows/initial_seed_setup_flow.py | 27 ++++++++++++++----- ports/stm32/boards/Passport/modules/stash.py | 20 +++++++++----- .../modules/tasks/get_seed_words_task.py | 2 +- .../Passport/modules/tasks/save_seed_task.py | 17 +++++++----- ports/stm32/boards/Passport/modules/utils.py | 5 +++- 6 files changed, 62 insertions(+), 32 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 707d9c476..7fc451511 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -211,7 +211,7 @@ def load(self): def get(self, kn, default=None): if self.in_overrides(kn): - return self.get_from_overrides(kn) + return self.get_from_overrides(kn, default) else: # Special case for xfp and xpub -- make sure they exist and create if not if not self.in_current(kn): @@ -233,7 +233,7 @@ def get(self, kn, default=None): finally: # system.hide_busy_bar() # These are overrides, so return them from there - return self.get_from_overrides(kn) + return self.get_from_overrides(kn, default) return self.get_from_current(kn, default) @@ -244,9 +244,10 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode and kn not in DEVICE_SETTINGS: + if self.temporary_mode: self.temporary_settings[kn] = v - return + if kn not in DEVICE_SETTINGS: + return self.current[kn] = v self.changed() @@ -256,7 +257,7 @@ def get_from_current(self, kn, default): return self.temporary_settings.get(kn, default) return self.current.get(kn, default) - def get_from_overrides(kn, default): + def get_from_overrides(self, kn, default): if self.temporary_mode: return self.temporary_overrides.get(kn, default) return self.overrides.get(kn, default) @@ -342,7 +343,7 @@ async def write_out(self): # Was sometimes running low on memory in this area: recover try: gc.collect() - self.save() + self.internal_save() except MemoryError: call_later_ms(250, self.write_out()) @@ -392,14 +393,16 @@ def erase_all(self): self.erase_cache_entry(pos) self.blank() - def save(self): - if self.temporary_mode: - return - + def internal_save(self): # Make two saves in case one is corrupted self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) + def save(self): + if self.temporary_mode: + return + self.internal_save() + def do_save(self, erase_old_pos=True): # print('do_save({})'.format(erase_old_pos)) # render as JSON, encrypt and write it. diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 5c9838680..6bcfcac07 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -5,13 +5,17 @@ import lvgl as lv from flows import Flow +from common import settings class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True): + def __init__(self, allow_backtrack=True, temporary=False): super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack + self.temporary = temporary + if temporary: + settings.enter_temporary_mode() async def show_intro(self): from pages import InfoPage @@ -19,7 +23,7 @@ async def show_intro(self): import microns # Pass silently if seed already exists - if has_seed(): + if has_seed() and not self.temporary: self.set_result(True) return @@ -35,6 +39,8 @@ async def show_intro(self): if result: self.goto(self.show_seed_setup_menu) else: + if self.temporary: + settings.exit_temporary_mode() self.set_result(None) async def show_seed_setup_menu(self): @@ -42,10 +48,19 @@ async def show_seed_setup_menu(self): from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow import microns - options = [{'label': 'Create New Seed', - 'value': lambda: NewSeedFlow(full_backup=True)}, - {'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, - {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}] + options = [] + + if not self.temporary: + options.append({'label': 'Create New Seed', + 'value': lambda: NewSeedFlow(full_backup=True)}) + + options.extend([{'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, + {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}]) + + if not self.temporary: + options.append({'label': 'Temporary Seed', + 'value': lambda: InitialSeedSetupFlow(allow_backtrack=self.allow_backtrack, + temporary=True)}) flow = await ChooserPage( text=None, diff --git a/ports/stm32/boards/Passport/modules/stash.py b/ports/stm32/boards/Passport/modules/stash.py index 04a58feaa..480b199b4 100644 --- a/ports/stm32/boards/Passport/modules/stash.py +++ b/ports/stm32/boards/Passport/modules/stash.py @@ -130,17 +130,23 @@ class SensitiveValues: # be a context manager, and holder to secrets in-memory def __init__(self, secret=None, for_backup=False): - from common import system + from common import system, settings if secret is None: - # fetch the secret from bootloader/atecc508a - from common import pa + if settings.temporary_mode: + if settings.get('temporary_seed', None) is None: + raise ValueError('no temporary secrets yet') + self.secret = settings.get('temporary_seed', None) + self.spots = [] + else: + # fetch the secret from bootloader/atecc508a + from common import pa - if pa.is_secret_blank(): - raise ValueError('no secrets yet') + if pa.is_secret_blank(): + raise ValueError('no secrets yet') - self.secret = pa.fetch() - self.spots = [self.secret] + self.secret = pa.fetch() + self.spots = [self.secret] else: # sometimes we already know it # assert set(secret) != {0} diff --git a/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py b/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py index 383ef83e6..ff88449af 100644 --- a/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py @@ -19,4 +19,4 @@ async def get_seed_words_task(on_done): except Exception as e: # print('get_seed_words_task(): Exception: {}'.format(e)) # Unable to read seed! - await on_done(None, None, '{}'.format(e)) + await on_done(None, '{}'.format(e)) diff --git a/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py b/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py index 6f06e815b..e0812e4a1 100644 --- a/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py @@ -13,20 +13,23 @@ async def save_seed_task(on_done, seed_bits): - from common import pa + from common import pa, settings import stash try: secret = SecretStash.encode(seed_bits=seed_bits) - pa.change(new_secret=secret) + if settings.temporary_mode: + settings.set_volatile('temporary_seed', secret) + else: + pa.change(new_secret=secret) - # Recapture XFP, etc. for new secret - await pa.new_main_secret(secret) + # Recapture XFP, etc. for new secret + await pa.new_main_secret(secret) - # Check and reload secret - pa.reset() - pa.login() + # Check and reload secret + pa.reset() + pa.login() with stash.SensitiveValues() as sv: sv.capture_xpub(save=True) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 31811dc63..f79d7461e 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1155,7 +1155,10 @@ def set_list(lst, index, value): def has_seed(): - from common import pa + from common import pa, settings + + if settings.temporary_mode: + return settings.get('temporary_seed', None) is not None # pa.is_secret_blank() function returns True before we are logged in, which is not right. if not is_logged_in(): From 11184e4fb152c6e3d3b8a0c1ee39e4e655460fd6 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 5 Apr 2024 17:25:11 -0400 Subject: [PATCH 04/28] SFT-1728: temporarily restored from backups --- ports/stm32/boards/Passport/modules/ext_settings.py | 5 +---- .../Passport/modules/flows/restore_backup_flow.py | 3 +++ .../Passport/modules/tasks/restore_backup_task.py | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 7fc451511..a5c28938b 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -489,10 +489,7 @@ def enter_temporary_mode(self): return self.temporary_mode = True - - # Merge the overrides and persistent settings into a new dict - self.temporary_settings.update(self.current) - self.temporary_overrides.update(self.overrides) + self.temporary_settings = self.default_values() def exit_temporary_mode(self): self.temporary_mode = False diff --git a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py index c767706eb..9be60deed 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py @@ -125,6 +125,7 @@ async def do_restore(self): from utils import start_task from flows import AutoBackupFlow, BackupFlow from pages import InfoPage + from common import settings # TODO: Change from spinner to ProgressPage and pass on_progress instead of None below. (error,) = await spinner_task( @@ -142,6 +143,8 @@ async def do_restore(self): if error_2 is not None or self.backup_code != new_backup_code: await InfoPage("You will receive a new Backup Code to use with your new Passport.").show() await BackupFlow(initial_backup=True).run() + else: # No error and self.backup_code == new_backup_code + settings.set('backup_quiz', True) elif self.autobackup: await AutoBackupFlow(offer=True).run() diff --git a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py index f2a6afc10..7cded7282 100644 --- a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py @@ -107,11 +107,14 @@ async def restore_backup_task(on_done, decryption_password, backup_file_path): await on_done(Error.CORRUPT_BACKUP_FILE) return - # Set the secret into the Secure Element - pa.change(new_secret=raw) - - # Force the right chain and update XFP & XPUB - await pa.new_main_secret(raw, chain) + if settings.temporary_mode: + settings.set_volatile('temporary_seed', raw) + else: + # Set the secret into the Secure Element + pa.change(new_secret=raw) + + # Force the right chain and update XFP & XPUB + await pa.new_main_secret(raw, chain) # Finally, restore the settings for idx, k in enumerate(vals): From f5c9070beb4044eb52a93957e8e85edbc07cacc5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 8 Apr 2024 12:34:38 -0400 Subject: [PATCH 05/28] SFT-1728: accounted for device settings --- .../boards/Passport/modules/ext_settings.py | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index a5c28938b..ad63cdc89 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -244,65 +244,63 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode: - self.temporary_settings[kn] = v - if kn not in DEVICE_SETTINGS: - return - - self.current[kn] = v - self.changed() + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.current[kn] = v + self.changed() + return + self.temporary_settings[kn] = v def get_from_current(self, kn, default): - if self.temporary_mode: - return self.temporary_settings.get(kn, default) - return self.current.get(kn, default) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return self.current.get(kn, default) + return self.temporary_settings.get(kn, default) def get_from_overrides(self, kn, default): - if self.temporary_mode: - return self.temporary_overrides.get(kn, default) - return self.overrides.get(kn, default) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return self.overrides.get(kn, default) + return self.temporary_overrides.get(kn, default) def set_volatile(self, kn, v): - if self.temporary_mode: - self.temporary_overrides[kn] = v + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.overrides[kn] = v return - self.overrides[kn] = v + self.temporary_overrides[kn] = v def in_current(self, kn): - if self.temporary_mode: - return kn in self.temporary_settings - return kn in self.current + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return kn in self.current + return kn in self.temporary_settings def in_overrides(self, kn): - if self.temporary_mode: - return kn in self.temporary_overrides - return kn in self.overrides + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return kn in self.overrides + return kn in self.temporary_overrides def clear_volatile(self, kn): - if self.temporary_mode: - if kn in self.temporary_overrides: - del self.temporary_overrides[kn] + if not self.temporary_mode or kn in DEVICE_SETTINGS: + if kn in self.overrides: + del self.overrides[kn] return - if kn in self.overrides: - del self.overrides[kn] + if kn in self.temporary_overrides: + del self.temporary_overrides[kn] def remove(self, kn): # print('remove(\'{}\') called!'.format(kn)) if self.in_current(kn): - if self.temporary_mode: - self.temporary_settings.pop(kn, None) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.current.pop(kn, None) + self.changed() return - self.current.pop(kn, None) - self.changed() + self.temporary_settings.pop(kn, None) def remove_regex(self, pattern): import re pattern = re.compile(pattern) matches = [] - if self.temporary_mode: - matches = [k for k in self.temporary_settings if pattern.search(k)] - else: + if self.temporary_mode or kn in DEVICE_SETTINGS: matches = [k for k in self.current if pattern.search(k)] + else: + matches = [k for k in self.temporary_settings if pattern.search(k)] for k in matches: self.remove(k) From 5ede4139452530fea8e82dee08fbd2c6d1cce8f7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 8 Apr 2024 22:46:43 -0400 Subject: [PATCH 06/28] SFT-1728: entering temporary seed after permanent setup --- ports/stm32/boards/Passport/manifest.py | 1 + .../boards/Passport/modules/flows/__init__.py | 1 + .../modules/flows/temporary_seed_flow.py | 28 +++++++++++++++++++ ports/stm32/boards/Passport/modules/menus.py | 9 ++++-- ports/stm32/boards/Passport/modules/utils.py | 14 ++++++---- 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index 62cb6d2eb..d0f41a531 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -152,6 +152,7 @@ 'flows/sign_psbt_microsd_flow.py', 'flows/sign_psbt_qr_flow.py', 'flows/sign_text_file_flow.py', + 'flows/temporary_seed_flow.py', 'flows/terms_of_use_flow.py', 'flows/update_firmware_flow.py', 'flows/verify_address_flow.py', diff --git a/ports/stm32/boards/Passport/modules/flows/__init__.py b/ports/stm32/boards/Passport/modules/flows/__init__.py index 2b8782c3e..952fff863 100644 --- a/ports/stm32/boards/Passport/modules/flows/__init__.py +++ b/ports/stm32/boards/Passport/modules/flows/__init__.py @@ -88,6 +88,7 @@ # Top-level flows need to be declared after other flows that they use from .initial_seed_setup_flow import * +from .temporary_seed_flow import * from .envoy_setup_flow import * from .manual_setup_flow import * from .select_setup_mode_flow import * diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py new file mode 100644 index 000000000..8b21b570b --- /dev/null +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# temporary_seed_flow.py - Start using a temporary seed + +from flows import Flow + + +class TemporarySeedFlow(Flow): + def __init__(self, clear=False): + initial_state = self.enter_seed if not clear else self.clear_seed + super().__init__(initial_state=initial_state, name='TemporarySeedFlow') + + async def enter_seed(self): + from flows import InitialSeedSetupFlow + result = await InitialSeedSetupFlow(temporary=True).run() + self.set_result(result) + + async def clear_seed(self): + from utils import spinner_task + from tasks import delay_task + from common import settings + from pages import SuccessPage + + await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) + await SuccessPage(text='Temporary seed cleared').show() + settings.exit_temporary_mode() + self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index ac8d63395..d5c6ddd00 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -131,8 +131,8 @@ def postmix_menu(): def plus_menu(): - from utils import is_passphrase_active - from flows import NewAccountFlow, ApplyPassphraseFlow + from utils import is_passphrase_active, has_temporary_seed, has_permanent_seed + from flows import NewAccountFlow, ApplyPassphraseFlow, TemporarySeedFlow return [ {'icon': 'ICON_ADD_ACCOUNT', 'label': 'New Account', 'flow': NewAccountFlow}, @@ -142,6 +142,11 @@ def plus_menu(): 'args': {'passphrase': ''}, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, {'icon': 'ICON_PASSPHRASE', 'label': 'Change Passphrase', 'flow': ApplyPassphraseFlow, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, + {'icon': 'ICON_PASSPHRASE', 'label': 'Enter Temporary Seed', 'flow': TemporarySeedFlow, + 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: not has_temporary_seed()}, + {'icon': 'ICON_PASSPHRASE', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, + 'args': {'clear': True}, 'statusbar': {'title': 'TEMPORARY SEED'}, + 'is_visible': lambda: has_temporary_seed() and has_permanent_seed()}, ] diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index f79d7461e..84be6c26f 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1154,17 +1154,21 @@ def set_list(lst, index, value): lst[index] = value -def has_seed(): - from common import pa, settings +def has_temporary_seed(): + if common.settings.temporary_mode: + return common.settings.get('temporary_seed', None) is not None + return False - if settings.temporary_mode: - return settings.get('temporary_seed', None) is not None +def has_permanent_seed(): # pa.is_secret_blank() function returns True before we are logged in, which is not right. if not is_logged_in(): return False + return not common.pa.is_secret_blank() - return not pa.is_secret_blank() + +def has_seed(): + return has_temporary_seed() or has_permanent_seed() def is_logged_in(): From c1f346a4a9790da1f91a1e306e8d9189167428c7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 9 Apr 2024 17:28:56 -0400 Subject: [PATCH 07/28] SFT-1728: entering temporary mode from key manager, still needs UI work --- .../modules/flows/export_derived_key_flow.py | 1 + .../modules/flows/initial_seed_setup_flow.py | 35 +++++++++++++- .../modules/flows/temporary_seed_flow.py | 48 ++++++++++++++++--- ports/stm32/boards/Passport/modules/menus.py | 7 ++- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index a47ede1fc..64f6b431d 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -23,6 +23,7 @@ async def generate_key(self): from flows import ViewSeedWordsFlow from public_constants import DIR_KEY_MNGR + # TODO: make this process a util function self.key_type = get_key_type_from_tn(self.key['tn']) if not self.key_type: diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 6bcfcac07..adaa69396 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -9,13 +9,18 @@ class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True, temporary=False): - super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') + def __init__(self, allow_backtrack=True, temporary=False, external_key=None): self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack self.temporary = temporary + self.external_key = external_key + initial_state = self.show_intro if temporary: settings.enter_temporary_mode() + # TODO: go to "explain_temporary" first + if self.external_key: + initial_state = self.apply_external_key + super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): from pages import InfoPage @@ -76,3 +81,29 @@ async def show_seed_setup_menu(self): return else: self.set_result(True) + + async def apply_external_key(self): + from utils import spinner_task + from tasks import save_seed_task + from common import ui + from pages import SuccessPage + + # Only allowed in temporary mode + if not self.temporary: + # TODO: add error messaging + print("setting external key in permanent mode") + self.set_result(False) + return + + (error,) = await spinner_task('Saving seed', save_seed_task, args=[self.external_key]) + + if error is not None: + options.exit_temporary_mode() + self.set_result(None) + return + + await SuccessPage(text='Temporary seed applied.').show() + + ui.full_cards_refresh() + await self.wait_to_die() + self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 8b21b570b..de3fef85e 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -7,8 +7,15 @@ class TemporarySeedFlow(Flow): - def __init__(self, clear=False): - initial_state = self.enter_seed if not clear else self.clear_seed + def __init__(self, context=None, clear=False): + self.key = context + print("context: {}".format(context)) + self.pk = None + initial_state = self.enter_seed + if self.key is not None: + initial_state = self.use_child_seed + elif clear: + initial_state = self.clear_seed super().__init__(initial_state=initial_state, name='TemporarySeedFlow') async def enter_seed(self): @@ -17,12 +24,41 @@ async def enter_seed(self): self.set_result(result) async def clear_seed(self): - from utils import spinner_task + from utils import spinner_task, start_task from tasks import delay_task - from common import settings + from common import settings, ui from pages import SuccessPage await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() - self.set_result(True) + await SuccessPage(text='Temporary seed cleared').show() + + # TODO: ui is still buggy when clearing a seed + ui.full_cards_refresh() + await self.wait_to_die() + self.set_result(False) + + async def use_child_seed(self): + from derived_key import get_key_type_from_tn + from utils import spinner_task + from pages import ErrorPage + from flows import InitialSeedSetupFlow + + self.key_type = get_key_type_from_tn(self.key['tn']) + + if not self.key_type: + await ErrorPage("Invalid key type number: {}".format(self.key['tn'])).show() + self.set_result(False) + return + + (vals, error) = await spinner_task(text='Generating key', + task=self.key_type['task'], + args=[self.key['index']]) + self.pk = vals['priv'] + if error is not None: + await ErrorPage(error).show() + self.set_result(False) + return + + result = await InitialSeedSetupFlow(temporary=True, external_key=self.pk).run() + self.set_result(result) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index d5c6ddd00..b5c3e0937 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -179,16 +179,19 @@ def backup_menu(): def key_item_menu(): - from utils import toggle_key_hidden, is_key_hidden + from utils import toggle_key_hidden, is_key_hidden, has_temporary_seed from flows import ( ViewDerivedKeyDetailsFlow, RenameDerivedKeyFlow, - ExportDerivedKeyFlow) + ExportDerivedKeyFlow, + TemporarySeedFlow) return [ {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, + {'icon': 'ICON_SEED', 'label': 'Use as Temporary Seed', 'flow': TemporarySeedFlow, + 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_ERASE', 'label': 'Hide Key', 'action': lambda item, context: toggle_key_hidden(item, context), From e5b79c0ea1bfc8142470e3ab23c3fd56ef0138f5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 10 Apr 2024 13:41:51 -0400 Subject: [PATCH 08/28] SFT-1728: fixed simulator in temporary mode, improved temporary mode UI --- .../boards/Passport/modules/ext_settings.py | 8 ++-- .../modules/flows/initial_seed_setup_flow.py | 41 ++++++++++++++----- .../modules/flows/restore_seed_flow.py | 10 +++-- simulator/sim_modules/pincodes.py | 28 ++++++------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index ad63cdc89..9e4fcc942 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -242,9 +242,9 @@ def changed(self): if self.is_dirty < 2 and self.loop: call_later_ms(250, self.write_out()) - def set(self, kn, v): + def set(self, kn, v, permanent=False): # print('set({}, {}'.format(kn, v)) - if not self.temporary_mode or kn in DEVICE_SETTINGS: + if (not self.temporary_mode or kn in DEVICE_SETTINGS) or permanent: self.current[kn] = v self.changed() return @@ -396,8 +396,8 @@ def internal_save(self): self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) - def save(self): - if self.temporary_mode: + def save(self, permanent=False): + if self.temporary_mode and not permanent: return self.internal_save() diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index adaa69396..1d861f69b 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -17,9 +17,7 @@ def __init__(self, allow_backtrack=True, temporary=False, external_key=None): initial_state = self.show_intro if temporary: settings.enter_temporary_mode() - # TODO: go to "explain_temporary" first - if self.external_key: - initial_state = self.apply_external_key + initial_state = self.explain_temporary super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): @@ -28,7 +26,7 @@ async def show_intro(self): import microns # Pass silently if seed already exists - if has_seed() and not self.temporary: + if has_seed(): self.set_result(True) return @@ -44,10 +42,34 @@ async def show_intro(self): if result: self.goto(self.show_seed_setup_menu) else: - if self.temporary: - settings.exit_temporary_mode() self.set_result(None) + async def explain_temporary(self): + from pages import InfoPage + import microns + + if self.allow_backtrack: + left_micron = microns.Back + else: + left_micron = None + + result = await InfoPage( + icon=lv.LARGE_ICON_SEED, + text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + left_micron=left_micron, + right_micron=microns.Forward).show() + + if not result: + settings.exit_temporary_mode() + self.set_result(None) + return + + if self.external_key: + self.goto(self.apply_external_key) + return + + self.goto(self.show_seed_setup_menu) + async def show_seed_setup_menu(self): from pages import ChooserPage from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow @@ -86,16 +108,15 @@ async def apply_external_key(self): from utils import spinner_task from tasks import save_seed_task from common import ui - from pages import SuccessPage + from pages import SuccessPage, ErrorPage # Only allowed in temporary mode if not self.temporary: - # TODO: add error messaging - print("setting external key in permanent mode") + await ErrorPage('Unable to use an external key in temporary mode').show() self.set_result(False) return - (error,) = await spinner_task('Saving seed', save_seed_task, args=[self.external_key]) + (error,) = await spinner_task('Applying seed', save_seed_task, args=[self.external_key]) if error is not None: options.exit_temporary_mode() diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 64824777e..5e333cdd1 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -166,13 +166,16 @@ async def invalid_seed(self): async def valid_seed(self): from flows import AutoBackupFlow, BackupFlow from utils import get_seed_from_words + from common import settings entropy = get_seed_from_words(self.mnemonic) - (error,) = await spinner_task('Saving seed', save_seed_task, args=[entropy]) + text = '{} seed'.format('Applying' if settings.temporary_mode else 'Saving') + (error,) = await spinner_task(text, save_seed_task, args=[entropy]) if error is None: import common - await SuccessPage(text='New seed imported and saved.').show() + text = 'New seed imported and {}'.format('applied' if settings.temporary_mode else 'saved') + await SuccessPage(text=text).show() if self.full_backup: await BackupFlow(initial_backup=True).run() elif self.autobackup: @@ -185,5 +188,6 @@ async def valid_seed(self): else: self.set_result(True) else: - await ErrorPage('Unable to save seed.').show() + text = 'Unable to {} seed'.format('apply' if settings.temporary_mode else 'save') + await ErrorPage(text).show() self.set_result(False) diff --git a/simulator/sim_modules/pincodes.py b/simulator/sim_modules/pincodes.py index 478181593..60f164953 100644 --- a/simulator/sim_modules/pincodes.py +++ b/simulator/sim_modules/pincodes.py @@ -139,8 +139,8 @@ def supply_chain_validation_words(challenge_str): return rv[0:4] # Only keep 4 words for supply chain validation def is_blank(self): - self.pin = common.settings.get('__pin__', '') - pin = common.settings.get('__pin__', '') + self.pin = common.settings.current.get('__pin__', '') + pin = common.settings.current.get('__pin__', '') return pin == '' def is_successful(self): @@ -162,14 +162,14 @@ def setup(self, pin, secondary=False): from common import settings # print('Setting up pin for login attempt') self.pin = pin - self.attempts_left = settings.get( + self.attempts_left = settings.current.get( '__attempts_left__', MAX_PIN_ATTEMPTS) return 0 def login(self): from common import settings - curr_pin = settings.get('__pin__', '') + curr_pin = settings.current.get('__pin__', '') self.is_logged_in = False # print('login(): self.pin={}, curr_pin={}'.format(self.pin, curr_pin)) @@ -181,14 +181,14 @@ def login(self): self.num_fails += 1 if self.attempts_left > MAX_PIN_ATTEMPTS: self.attempts_left = MAX_PIN_ATTEMPTS - settings.set('__attempts_left__', self.attempts_left) + settings.set('__attempts_left__', self.attempts_left, True) raise RuntimeError() else: # Reset to 21 when successfully logged in self.attempts_left = MAX_PIN_ATTEMPTS self.is_logged_in = True self.num_fails = 0 - settings.set('__attempts_left__', MAX_PIN_ATTEMPTS) + settings.set('__attempts_left__', MAX_PIN_ATTEMPTS, True) return self.is_logged_in @@ -198,7 +198,7 @@ def change(self, **kwargs): new_secret = kwargs.get('new_secret', None) if new_secret == None: - curr_pin = settings.get('__pin__', '').encode() + curr_pin = settings.current.get('__pin__', '').encode() old_pin = kwargs.get('old_pin', '').encode() new_pin = kwargs.get('new_pin', '').encode() @@ -209,12 +209,12 @@ def change(self, **kwargs): # print('new_pin={}, len={}'.format(new_pin, len(new_pin))) if isinstance(new_pin, (bytes, bytearray)) and is_all_zero(new_pin): # print('RECEIVED BLANK PIN!') - settings.set('__pin__', '') + settings.set('__pin__', '', True) else: - settings.set('__pin__', new_pin.decode()) + settings.set('__pin__', new_pin.decode(), True) # We have to save because the caller tries to reset before the normal save timeout expires - settings.save() + settings.save(True) else: # print('change(): {}'.format(new_secret)) @@ -224,13 +224,13 @@ def save_secret(self, buf): from common import settings str = b2a_hex(buf).decode('utf-8') # print('save_secret: {}'.format(str)) - settings.set('__secret__', str) - settings.save() + settings.set('__secret__', str, True) + settings.save(True) self.load_secret() def load_secret(self): from common import settings - str = settings.get('__secret__', ZERO_SECRET_STR) + str = settings.current.get('__secret__', ZERO_SECRET_STR) self.secret = a2b_hex(str.encode('utf-8')) def fetch(self): @@ -250,7 +250,7 @@ async def new_main_secret(self, raw_secret, chain=None): # TODO: Is this comment still accurate? # We shouldn't need to save this anymore since we are dynamically managing xfp and xpub - # settings.save() + # settings.save(True) # Set the key for the flash cache (cache was inaccessible prior to user logging in) flash_cache.set_key(new_secret=raw_secret) From d14e9a2f77e29ec548fb17c72fb0993d9cab505a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 11 Apr 2024 20:50:47 -0400 Subject: [PATCH 09/28] SFT-1728: moved temporary functionality into temporary_seed_flow --- .../modules/flows/initial_seed_setup_flow.py | 79 ++----------------- .../modules/flows/restore_seed_flow.py | 58 ++++++++++++-- .../modules/flows/temporary_seed_flow.py | 64 +++++++++++---- ports/stm32/boards/Passport/modules/menus.py | 6 +- 4 files changed, 109 insertions(+), 98 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 1d861f69b..5c9838680 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -5,20 +5,13 @@ import lvgl as lv from flows import Flow -from common import settings class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True, temporary=False, external_key=None): + def __init__(self, allow_backtrack=True): + super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack - self.temporary = temporary - self.external_key = external_key - initial_state = self.show_intro - if temporary: - settings.enter_temporary_mode() - initial_state = self.explain_temporary - super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): from pages import InfoPage @@ -44,50 +37,15 @@ async def show_intro(self): else: self.set_result(None) - async def explain_temporary(self): - from pages import InfoPage - import microns - - if self.allow_backtrack: - left_micron = microns.Back - else: - left_micron = None - - result = await InfoPage( - icon=lv.LARGE_ICON_SEED, - text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', - left_micron=left_micron, - right_micron=microns.Forward).show() - - if not result: - settings.exit_temporary_mode() - self.set_result(None) - return - - if self.external_key: - self.goto(self.apply_external_key) - return - - self.goto(self.show_seed_setup_menu) - async def show_seed_setup_menu(self): from pages import ChooserPage from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow import microns - options = [] - - if not self.temporary: - options.append({'label': 'Create New Seed', - 'value': lambda: NewSeedFlow(full_backup=True)}) - - options.extend([{'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, - {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}]) - - if not self.temporary: - options.append({'label': 'Temporary Seed', - 'value': lambda: InitialSeedSetupFlow(allow_backtrack=self.allow_backtrack, - temporary=True)}) + options = [{'label': 'Create New Seed', + 'value': lambda: NewSeedFlow(full_backup=True)}, + {'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, + {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}] flow = await ChooserPage( text=None, @@ -103,28 +61,3 @@ async def show_seed_setup_menu(self): return else: self.set_result(True) - - async def apply_external_key(self): - from utils import spinner_task - from tasks import save_seed_task - from common import ui - from pages import SuccessPage, ErrorPage - - # Only allowed in temporary mode - if not self.temporary: - await ErrorPage('Unable to use an external key in temporary mode').show() - self.set_result(False) - return - - (error,) = await spinner_task('Applying seed', save_seed_task, args=[self.external_key]) - - if error is not None: - options.exit_temporary_mode() - self.set_result(None) - return - - await SuccessPage(text='Temporary seed applied.').show() - - ui.full_cards_refresh() - await self.wait_to_die() - self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 5e333cdd1..24e26b606 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -10,11 +10,12 @@ from utils import spinner_task, insufficient_randomness from tasks import save_seed_task from public_constants import SEED_LENGTHS +import lvgl as lv class RestoreSeedFlow(Flow): - def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False): - super().__init__(initial_state=self.choose_restore_method, name='RestoreSeedFlow') + def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False, + temporary=False): self.refresh_cards_when_done = refresh_cards_when_done self.seed_format = None self.seed_length = None @@ -24,6 +25,49 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.full_backup = full_backup self.autobackup = autobackup self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} + self.temporary = temporary + + initial_state = self.choose_temporary + if self.temporary: + initial_state = self.explain_temporary + + super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + + async def choose_temporary(self): + from pages import ChooserPage + + text = 'Would you like to make a permanent seed, or a temporary seed?' + options = [{'label': 'Permanent', 'value': True}, + {'label': 'Temporary', 'value': False}] + + permanent = await ChooserPage(text=text, + card_header={'title': 'Seed Type'}, + options=options).show() + if permanent is None: + self.set_result(False) + return + + if permanent: + self.goto(self.choose_restore_method) + else: + self.goto(self.explain_temporary) + + async def explain_temporary(self): + from pages import InfoPage + import microns + + result = await InfoPage( + icon=lv.LARGE_ICON_SEED, + text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + left_micron=microns.Back, + right_micron=microns.Forward).show() + + if not result: + if not self.back(): + self.set_result(None) + return + + self.goto(self.choose_restore_method) async def choose_restore_method(self): from pages import ChooserPage @@ -37,7 +81,8 @@ async def choose_restore_method(self): choice = await ChooserPage(card_header={'title': 'Seed Format'}, options=options).show() if choice is None: - self.set_result(False) + if not self.back(): + self.set_result(False) return self.seed_format = choice @@ -166,15 +211,14 @@ async def invalid_seed(self): async def valid_seed(self): from flows import AutoBackupFlow, BackupFlow from utils import get_seed_from_words - from common import settings entropy = get_seed_from_words(self.mnemonic) - text = '{} seed'.format('Applying' if settings.temporary_mode else 'Saving') + text = '{} seed'.format('Applying' if self.temporary else 'Saving') (error,) = await spinner_task(text, save_seed_task, args=[entropy]) if error is None: import common - text = 'New seed imported and {}'.format('applied' if settings.temporary_mode else 'saved') + text = 'New seed imported and {}'.format('applied' if self.temporary else 'saved') await SuccessPage(text=text).show() if self.full_backup: await BackupFlow(initial_backup=True).run() @@ -188,6 +232,6 @@ async def valid_seed(self): else: self.set_result(True) else: - text = 'Unable to {} seed'.format('apply' if settings.temporary_mode else 'save') + text = 'Unable to {} seed'.format('apply' if self.temporary else 'save') await ErrorPage(text).show() self.set_result(False) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index de3fef85e..a211a76f2 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -4,45 +4,68 @@ # temporary_seed_flow.py - Start using a temporary seed from flows import Flow +from common import settings, ui class TemporarySeedFlow(Flow): def __init__(self, context=None, clear=False): self.key = context - print("context: {}".format(context)) - self.pk = None initial_state = self.enter_seed - if self.key is not None: - initial_state = self.use_child_seed - elif clear: + if clear: initial_state = self.clear_seed + elif self.key is not None: + initial_state = self.use_child_seed super().__init__(initial_state=initial_state, name='TemporarySeedFlow') + # Override set_result to safely exit temporary mode + def set_result(self, result, forget_state=True): + if not result: + settings.exit_temporary_mode() + super().set_result(result, forget_state) + async def enter_seed(self): - from flows import InitialSeedSetupFlow - result = await InitialSeedSetupFlow(temporary=True).run() + from flows import RestoreSeedFlow + + settings.enter_temporary_mode() + result = await RestoreSeedFlow(refresh_cards_when_done=True, + autobackup=False, + full_backup=True, + temporary=True).run() + print("temporary_settings: {}".format(settings.temporary_settings)) + print("current: {}".format(settings.current)) + print("temporary_mode: {}".format(settings.temporary_mode)) self.set_result(result) + print("temporary_mode: {}".format(settings.temporary_mode)) async def clear_seed(self): from utils import spinner_task, start_task from tasks import delay_task - from common import settings, ui from pages import SuccessPage await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) - settings.exit_temporary_mode() await SuccessPage(text='Temporary seed cleared').show() + settings.exit_temporary_mode() # TODO: ui is still buggy when clearing a seed + self.set_result(True) ui.full_cards_refresh() await self.wait_to_die() - self.set_result(False) async def use_child_seed(self): from derived_key import get_key_type_from_tn from utils import spinner_task - from pages import ErrorPage - from flows import InitialSeedSetupFlow + from tasks import save_seed_task + from pages import ErrorPage, SuccessPage, InfoPage + import microns + + # TODO: add page explaining child seed as temporary seed, give user change to back out + + result = await InfoPage('{} will be used as a temporary seed'.format(self.key['name']), + left_micron=microns.Back).show() + + if not result: + self.set_result(False) + return self.key_type = get_key_type_from_tn(self.key['tn']) @@ -54,11 +77,22 @@ async def use_child_seed(self): (vals, error) = await spinner_task(text='Generating key', task=self.key_type['task'], args=[self.key['index']]) - self.pk = vals['priv'] + pk = vals['priv'] if error is not None: await ErrorPage(error).show() self.set_result(False) return - result = await InitialSeedSetupFlow(temporary=True, external_key=self.pk).run() - self.set_result(result) + print('pk: {}'.format(pk)) + settings.enter_temporary_mode() + (error,) = await spinner_task('Applying seed', save_seed_task, args=[pk]) + + if error is not None: + self.set_result(None) + return + + await SuccessPage(text='Temporary seed applied.').show() + + self.set_result(True) + ui.full_cards_refresh() + await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index b5c3e0937..fed053ecb 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -142,9 +142,9 @@ def plus_menu(): 'args': {'passphrase': ''}, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, {'icon': 'ICON_PASSPHRASE', 'label': 'Change Passphrase', 'flow': ApplyPassphraseFlow, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, - {'icon': 'ICON_PASSPHRASE', 'label': 'Enter Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Temporary Seed', 'flow': TemporarySeedFlow, 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: not has_temporary_seed()}, - {'icon': 'ICON_PASSPHRASE', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, 'args': {'clear': True}, 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: has_temporary_seed() and has_permanent_seed()}, ] @@ -190,7 +190,7 @@ def key_item_menu(): {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, - {'icon': 'ICON_SEED', 'label': 'Use as Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Temporary Seed', 'flow': TemporarySeedFlow, 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_ERASE', 'label': 'Hide Key', From ca6b8c1576984c482e916409937134bc7858abad Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 18 Apr 2024 16:52:43 -0400 Subject: [PATCH 10/28] SFT-1728: incorporated comments and ui improvements --- .../modules/flows/random_final_word_flow.py | 4 +++- .../Passport/modules/flows/restore_seed_flow.py | 14 +++++++++----- .../Passport/modules/flows/temporary_seed_flow.py | 2 +- .../boards/Passport/modules/pages/chooser_page.py | 5 +++-- ports/stm32/boards/Passport/modules/ui/ui.py | 2 +- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py b/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py index 2fa6c5685..26b21ce03 100644 --- a/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py @@ -31,10 +31,12 @@ async def generate_word(self): import microns from utils import recolor from styles.colors import HIGHLIGHT_TEXT_HEX + from common import settings last_word = get_last_word(self.selected_words) styled_last_word = recolor(HIGHLIGHT_TEXT_HEX, last_word) - text = 'Your final word is\n{}.\n\nImport and save this seed?'.format(styled_last_word) + save_wording = ' and save' if not settings.temporary_mode else '' + text = 'Your final word is\n{}.\n\nImport{} this seed?'.format(styled_last_word, save_wording) result = await InfoPage(text=text, left_micron=microns.Retry).show() diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 24e26b606..13089dbec 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -11,6 +11,7 @@ from tasks import save_seed_task from public_constants import SEED_LENGTHS import lvgl as lv +from common import settings class RestoreSeedFlow(Flow): @@ -36,13 +37,14 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F async def choose_temporary(self): from pages import ChooserPage - text = 'Would you like to make a permanent seed, or a temporary seed?' + text = 'Import and save a seed, or import one temporarily?' options = [{'label': 'Permanent', 'value': True}, {'label': 'Temporary', 'value': False}] permanent = await ChooserPage(text=text, - card_header={'title': 'Seed Type'}, - options=options).show() + icon=lv.LARGE_ICON_QUESTION, + options=options, + icon_pad=-6).show() if permanent is None: self.set_result(False) return @@ -58,7 +60,7 @@ async def explain_temporary(self): result = await InfoPage( icon=lv.LARGE_ICON_SEED, - text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + text='Temporary seeds are not saved to Passport. Ensure you have a robust backup in place.', left_micron=microns.Back, right_micron=microns.Forward).show() @@ -174,7 +176,9 @@ async def enter_seed_words(self): self.seed_words.append(last_word) if insufficient_randomness(self.seed_words): - text = "This seed contains 3 or more repeat words and may put funds at risk.\n\nSave seed and continue?" + save_wording = 'Save' if not settings.temporary_mode else 'Import' + text = "This seed contains 3 or more repeat words and may put funds at risk.\n\n{} seed and continue?" \ + .format(save_wording) result2 = await ErrorPage(text=text, left_micron=microns.Cancel).show() diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index a211a76f2..eb7da215f 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -42,7 +42,7 @@ async def clear_seed(self): from tasks import delay_task from pages import SuccessPage - await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) + await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() diff --git a/ports/stm32/boards/Passport/modules/pages/chooser_page.py b/ports/stm32/boards/Passport/modules/pages/chooser_page.py index 8be334cd5..175706223 100644 --- a/ports/stm32/boards/Passport/modules/pages/chooser_page.py +++ b/ports/stm32/boards/Passport/modules/pages/chooser_page.py @@ -23,7 +23,7 @@ def __init__( self, card_header=None, statusbar=None, options=[], initial_value=None, on_change=None, scroll_fix=False, icon=None, icon_color=CHOOSER_ICON, text=None, center=False, item_icon='ICON_SMALL_CHECKMARK', - left_micron=None, right_micron=None): + left_micron=None, right_micron=None, icon_pad=0): from views import ListItem, View import passport @@ -48,6 +48,7 @@ def __init__( self.text = text self.center = center self.item_icon = item_icon + self.icon_pad = icon_pad # If initial value is given, then select it, else select the first item if self.initial_value is not None: @@ -72,7 +73,7 @@ def __init__( with Stylize(self.icon_view) as default: if self.icon_color is not None: default.img_recolor(self.icon_color) - top = 20 if passport.IS_COLOR else 12 + top = (20 if passport.IS_COLOR else 12) + self.icon_pad default.pad(top=top, bottom=12) self.add_child(self.icon_view) diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index c097b6ced..e066b758b 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -255,7 +255,7 @@ def update_cards(self, # print('account[{}]={}'.format(account, i)) account_card = { - 'right_icon': 'ICON_BITCOIN', + 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_SEED', 'header_color': LIGHT_GREY, 'header_fg_color': LIGHT_TEXT, 'statusbar': {'title': 'ACCOUNT', 'icon': 'ICON_FOLDER', 'fg_color': get_account_fg(account)}, From d08d6a6e68782b9ce1c5a197f9d05e35b259183a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 20 May 2024 11:38:42 -0400 Subject: [PATCH 11/28] SFT-1728: improved temporary seed question --- .../boards/Passport/modules/flows/restore_seed_flow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 13089dbec..c94d3472b 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -37,9 +37,9 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F async def choose_temporary(self): from pages import ChooserPage - text = 'Import and save a seed, or import one temporarily?' - options = [{'label': 'Permanent', 'value': True}, - {'label': 'Temporary', 'value': False}] + text = 'Save this seed, or import temporarily?' + options = [{'label': 'Save Seed', 'value': True}, + {'label': 'Temporary Seed', 'value': False}] permanent = await ChooserPage(text=text, icon=lv.LARGE_ICON_QUESTION, From 166c1096caa036a745ff64f3bf6def3eb5f9b591 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 21 Jun 2024 16:51:40 -0400 Subject: [PATCH 12/28] SFT-1728: removed spinner when clearing temporary seed --- .../boards/Passport/modules/flows/temporary_seed_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index eb7da215f..302792e96 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -42,9 +42,9 @@ async def clear_seed(self): from tasks import delay_task from pages import SuccessPage - await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() + # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) + await SuccessPage(text='Temporary seed cleared').show() # TODO: ui is still buggy when clearing a seed self.set_result(True) From 41a577568feffbe6bc4ffa71fa8a3fb8965dca9e Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Jul 2024 16:36:31 -0400 Subject: [PATCH 13/28] SFT-1728: removed old TODOs --- .../boards/Passport/modules/flows/export_derived_key_flow.py | 1 - .../stm32/boards/Passport/modules/flows/temporary_seed_flow.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index 64f6b431d..a47ede1fc 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -23,7 +23,6 @@ async def generate_key(self): from flows import ViewSeedWordsFlow from public_constants import DIR_KEY_MNGR - # TODO: make this process a util function self.key_type = get_key_type_from_tn(self.key['tn']) if not self.key_type: diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 302792e96..57ff712c5 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -46,7 +46,6 @@ async def clear_seed(self): # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary seed cleared').show() - # TODO: ui is still buggy when clearing a seed self.set_result(True) ui.full_cards_refresh() await self.wait_to_die() @@ -58,8 +57,6 @@ async def use_child_seed(self): from pages import ErrorPage, SuccessPage, InfoPage import microns - # TODO: add page explaining child seed as temporary seed, give user change to back out - result = await InfoPage('{} will be used as a temporary seed'.format(self.key['name']), left_micron=microns.Back).show() From f8e9a7da4e8100f0c6506f97330070cb7dde8b96 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 15 Jul 2024 13:09:05 -0400 Subject: [PATCH 14/28] SFT-1728: first pass of hourglass icons, removed prints --- .../Passport/images/color/ICON_HOURGLASS.c | 54 ++++++++++++++++++ .../ICON_HOURGLASS__CF_INDEXED_2_BIT.png | Bin 0 -> 1365 bytes ports/stm32/boards/Passport/images/images.h | 3 + .../Passport/images/mono/ICON_HOURGLASS.c | 51 +++++++++++++++++ .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 51 +++++++++++++++++ ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 0 -> 3998 bytes .../mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png | Bin 0 -> 5944 bytes .../modules/flows/temporary_seed_flow.py | 5 -- ports/stm32/boards/Passport/modules/menus.py | 6 +- 9 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c create mode 100644 ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS.c create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c new file mode 100644 index 000000000..536133f08 --- /dev/null +++ b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x55, 0x55, 0x55, 0x50, + 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x00, 0x14, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x01, 0x00, + 0x00, 0x10, 0x14, 0x04, 0x00, + 0x00, 0x05, 0x14, 0x10, 0x00, + 0x00, 0x01, 0x01, 0x40, 0x00, + 0x00, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x40, 0x00, + 0x00, 0x04, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x14, 0x05, 0x00, + 0x01, 0x40, 0x55, 0x01, 0x00, + 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x05, 0x55, 0x50, 0x40, + 0x05, 0x55, 0x55, 0x55, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 116, + .data = ICON_HOURGLASS_map, +}; diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..67d9bc759ecb7efd85497c7d675e9d34561a01f3 GIT binary patch literal 1365 zcmV-b1*-aqP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KHlH4E+MgLhvmVhLL#Bwm0s_Y=k&jmlGduH;g zrM`$U1_a3H!INBYeR{C38q>o=eUVS1PG+#lYinmDN&At34jlIY-a>a8EE) zf?keCtEYX1Uf*80)}ZZKAN-tQKOLPDmqVE=GY0)JNa*>{Ux9?OAA?-n9z)&u=-U;s zzujWX_3dOnhet&+WT`8xgn1W|xLQ9Vka;Jm=nXB2!j2$SL^!O8>ywa>r_a0k7#|b# zl;mp^-e>NJ8-Y8aTeGq`eRp6 zuzor~imaQ#58HLLMD}Dk8@|;SB^xl{BD^^`Rxq5bU z_u{p15uAZqaWr_6fB3`b zN<}Ii>>#3$p*mR*6>-!m6rn<>6Q(9nI5-4Gifa9=`f`NX zFOS&(000J1OjJeSf>r +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x80, + 0x17, 0xfd, 0x00, + 0x13, 0xf9, 0x00, + 0x09, 0xf2, 0x00, + 0x04, 0x04, 0x00, + 0x02, 0x48, 0x00, + 0x01, 0x10, 0x00, + 0x00, 0xa0, 0x00, + 0x00, 0xa0, 0x00, + 0x01, 0x10, 0x00, + 0x02, 0x48, 0x00, + 0x04, 0xe4, 0x00, + 0x09, 0xf2, 0x00, + 0x13, 0xf9, 0x00, + 0x17, 0xfd, 0x00, + 0x3f, 0xff, 0x80, + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 68, + .data = ICON_HOURGLASS_map, +}; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c new file mode 100644 index 000000000..d6bf6ecb9 --- /dev/null +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x80, + 0x7f, 0xff, 0xc0, + 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0x80, + 0x1f, 0xff, 0x00, + 0x0f, 0xfe, 0x00, + 0x07, 0xfc, 0x00, + 0x03, 0xf8, 0x00, + 0x01, 0xf0, 0x00, + 0x01, 0xf0, 0x00, + 0x03, 0xf8, 0x00, + 0x07, 0xfc, 0x00, + 0x0f, 0xfe, 0x00, + 0x1f, 0xff, 0x00, + 0x3f, 0xff, 0x80, + 0x7f, 0xff, 0xc0, + 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0x80, + 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS_BACKGROUND = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 68, + .data = ICON_HOURGLASS_BACKGROUND_map, +}; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..e501dd4f0a5c02e3384ec3d86a1ce932641671eb GIT binary patch literal 3998 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 Ef{xjiIsgCw literal 0 HcmV?d00001 diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..845ca7f5b3c0f4a821b24bbb6274e9fd9732f400 GIT binary patch literal 5944 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 aMgjoGoe*gOApijY0000 Date: Tue, 16 Jul 2024 15:03:55 -0400 Subject: [PATCH 15/28] SFT-1728: prevented erasing from temporary mode, fixed normal erasing bug --- ports/stm32/boards/Passport/modules/ext_settings.py | 6 +++--- ports/stm32/boards/Passport/modules/menus.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 9e4fcc942..eae106440 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -297,10 +297,10 @@ def remove_regex(self, pattern): import re pattern = re.compile(pattern) matches = [] - if self.temporary_mode or kn in DEVICE_SETTINGS: - matches = [k for k in self.current if pattern.search(k)] - else: + if self.temporary_mode: matches = [k for k in self.temporary_settings if pattern.search(k)] + else: + matches = [k for k in self.current if pattern.search(k)] for k in matches: self.remove(k) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index e6c49f357..411a98c76 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -391,6 +391,7 @@ def developer_pubkey_menu(): def advanced_menu(): from flows import ViewSeedWordsFlow, ErasePassportFlow, ScvFlow, ShowSecurityWordsSettingFlow + from utils import has_temporary_seed return [ {'icon': 'ICON_SETTINGS', 'label': 'Security Words', 'flow': ShowSecurityWordsSettingFlow}, @@ -400,7 +401,8 @@ def advanced_menu(): {'icon': 'ICON_ONE_KEY', 'label': 'Developer Pubkey', 'submenu': developer_pubkey_menu, 'statusbar': {'title': 'DEV. PUBKEY'}}, {'icon': 'ICON_MICROSD', 'label': 'microSD', 'submenu': microsd_menu}, - {'icon': 'ICON_ERASE', 'label': 'Erase Passport', 'flow': ErasePassportFlow}, + {'icon': 'ICON_ERASE', 'label': 'Erase Passport', 'flow': ErasePassportFlow, + 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_SHIELD', 'label': 'Security Check', 'flow': ScvFlow, 'args': {'envoy': False, 'ask_to_skip': False}}, ] From 35102f0d9cc67b36d3afb96da9ba2fc37dc04c20 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 15:31:33 -0400 Subject: [PATCH 16/28] SFT-1728: hid restore backup in temporary mode, fixed backup code for temporary seeds --- ports/stm32/boards/Passport/modules/menus.py | 9 ++++----- .../Passport/modules/tasks/get_backup_code_task.py | 8 ++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 411a98c76..6883794e6 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -4,7 +4,7 @@ # menus.py - Menu configuration import lvgl as lv -from utils import has_seed +from utils import has_seed, has_temporary_seed # from pages import ColorPickerPage ######################################################################################## @@ -131,7 +131,7 @@ def postmix_menu(): def plus_menu(): - from utils import is_passphrase_active, has_temporary_seed, has_permanent_seed + from utils import is_passphrase_active, has_permanent_seed from flows import NewAccountFlow, ApplyPassphraseFlow, TemporarySeedFlow return [ @@ -171,7 +171,7 @@ def backup_menu(): return [ {'icon': 'ICON_BACKUP', 'label': 'Backup Now', 'flow': BackupFlow, 'is_visible': has_seed}, {'icon': 'ICON_RETRY', 'label': 'Restore', 'flow': RestoreBackupFlow, - 'args': {'refresh_cards_when_done': True}}, + 'args': {'refresh_cards_when_done': True}, 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_CIRCLE_CHECK', 'label': 'Verify Backup', 'flow': VerifyBackupFlow}, {'icon': 'ICON_PIN', 'label': 'View Backup Code', 'flow': ViewBackupCodeFlow, 'statusbar': {'title': 'BACKUP', 'icon': 'ICON_PIN'}, 'is_visible': has_seed} @@ -179,7 +179,7 @@ def backup_menu(): def key_item_menu(): - from utils import toggle_key_hidden, is_key_hidden, has_temporary_seed + from utils import toggle_key_hidden, is_key_hidden from flows import ( ViewDerivedKeyDetailsFlow, @@ -391,7 +391,6 @@ def developer_pubkey_menu(): def advanced_menu(): from flows import ViewSeedWordsFlow, ErasePassportFlow, ScvFlow, ShowSecurityWordsSettingFlow - from utils import has_temporary_seed return [ {'icon': 'ICON_SETTINGS', 'label': 'Security Words', 'flow': ShowSecurityWordsSettingFlow}, diff --git a/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py b/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py index 7c584787d..17c350145 100644 --- a/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py @@ -5,6 +5,7 @@ import trezorcrypto from micropython import const +from utils import has_temporary_seed # we make passwords with this number of words _NUM_DECIMAL_DIGITS_IN_BACKUP_CODE = const(20) @@ -16,11 +17,14 @@ def get_backup_code(): - from common import system, pa + from common import system, pa, settings device_hash = bytearray(32) system.get_device_hash(device_hash) - secret = pa.fetch() + if has_temporary_seed(): + secret = settings.get('temporary_seed') + else: + secret = pa.fetch() # print('secret: {}'.format(bytes_to_hex_str(secret))) hash = trezorcrypto.sha256() From b67eb9f56002ea25a125cf54789fe1332049baec Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 16:07:38 -0400 Subject: [PATCH 17/28] SFT-1728: prevented saving nostr keys as temporary seed --- ports/stm32/boards/Passport/modules/menus.py | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 6883794e6..eaf3e1cfd 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -178,7 +178,7 @@ def backup_menu(): ] -def key_item_menu(): +def seed_item_menu(): from utils import toggle_key_hidden, is_key_hidden from flows import ( @@ -200,6 +200,25 @@ def key_item_menu(): ] +def key_item_menu(): + from utils import toggle_key_hidden, is_key_hidden + + from flows import ( + ViewDerivedKeyDetailsFlow, + RenameDerivedKeyFlow, + ExportDerivedKeyFlow) + return [ + {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, + {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, + {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, + {'icon': 'ICON_ERASE', + 'label': 'Hide Key', + 'action': lambda item, context: toggle_key_hidden(item, context), + 'is_toggle': True, + 'value': lambda context: is_key_hidden(context)}, + ] + + def new_key_menu(): from flows import NewDerivedKeyFlow from derived_key import key_types @@ -260,7 +279,7 @@ def key_manager_menu(): result.append({'icon': key_type['icon'], 'label': title, - 'submenu': key_item_menu, + 'submenu': seed_item_menu if key_type['words'] else key_item_menu, 'card_header': {'title': title, 'right_icon': key_type['icon']}, 'statusbar': {'title': 'KEY MANAGER'}, From 51f906b0150ae45076e8169c72730411be2f3b22 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 17:15:19 -0400 Subject: [PATCH 18/28] SFT-1728: fixing wording and capitalization --- .../Passport/modules/flows/export_derived_key_flow.py | 2 +- .../boards/Passport/modules/flows/temporary_seed_flow.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index a47ede1fc..af3d13042 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -30,7 +30,7 @@ async def generate_key(self): self.set_result(False) return - (vals, error) = await spinner_task(text='Generating key', + (vals, error) = await spinner_task(text='Retrieving Key', task=self.key_type['task'], args=[self.key['index']]) self.pk = vals['priv'] diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index d0359f95a..3ed73f720 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -40,7 +40,7 @@ async def clear_seed(self): settings.exit_temporary_mode() # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() + await SuccessPage(text='Temporary Seed Cleared').show() self.set_result(True) ui.full_cards_refresh() @@ -67,7 +67,7 @@ async def use_child_seed(self): self.set_result(False) return - (vals, error) = await spinner_task(text='Generating key', + (vals, error) = await spinner_task(text='Retrieving Key', task=self.key_type['task'], args=[self.key['index']]) pk = vals['priv'] @@ -77,13 +77,13 @@ async def use_child_seed(self): return settings.enter_temporary_mode() - (error,) = await spinner_task('Applying seed', save_seed_task, args=[pk]) + (error,) = await spinner_task('Applying Seed', save_seed_task, args=[pk]) if error is not None: self.set_result(None) return - await SuccessPage(text='Temporary seed applied.').show() + await SuccessPage(text='Temporary Seed Applied.').show() self.set_result(True) ui.full_cards_refresh() From c84445b6c45cf4108fefda7c95d338921d760da9 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 14:04:04 -0400 Subject: [PATCH 19/28] SFT-1728: avoided scrollbar in key item menu --- ports/stm32/boards/Passport/modules/flows/menu_flow.py | 4 +++- ports/stm32/boards/Passport/modules/menus.py | 3 ++- ports/stm32/boards/Passport/modules/pages/menu_page.py | 7 +++++-- ports/stm32/boards/Passport/modules/views/menu_item.py | 9 +++++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/menu_flow.py b/ports/stm32/boards/Passport/modules/flows/menu_flow.py index 0593aaa4e..ba08d8be4 100644 --- a/ports/stm32/boards/Passport/modules/flows/menu_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/menu_flow.py @@ -14,7 +14,7 @@ class MenuFlow(Flow): latest_menu = None def __init__(self, menu, initial_selected_index=0, is_top_level=None, context=None, - card_header=None, statusbar=None, dynamic=None): + card_header=None, statusbar=None, dynamic=None, item_vertical_padding=None): self.menu = menu super().__init__(initial_state=self.show_menu, name='MenuFlow') @@ -26,6 +26,7 @@ def __init__(self, menu, initial_selected_index=0, is_top_level=None, context=No self.statusbar = statusbar self.dynamic = dynamic MenuFlow.latest_menu = self + self.item_vertical_padding = item_vertical_padding async def show_menu(self): from common import ui @@ -52,6 +53,7 @@ async def show_menu(self): right_micron=microns.Checkmark, is_top_level=self.is_top_level, context=self.context, + item_vertical_padding=self.item_vertical_padding ).show() if result is None: if ui.is_top_level(): diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index eaf3e1cfd..9f9199dd4 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -283,7 +283,8 @@ def key_manager_menu(): 'card_header': {'title': title, 'right_icon': key_type['icon']}, 'statusbar': {'title': 'KEY MANAGER'}, - 'args': {'context': key, 'dynamic': key_type.get('menu', None)}, + 'args': {'context': key, 'dynamic': key_type.get('menu', None), + 'item_vertical_padding': 9}, 'auto_card_header': False}) result.append({'icon': 'ICON_ONE_KEY', 'label': 'New Key', 'submenu': new_key_menu}) diff --git a/ports/stm32/boards/Passport/modules/pages/menu_page.py b/ports/stm32/boards/Passport/modules/pages/menu_page.py index 767996a02..749040c17 100644 --- a/ports/stm32/boards/Passport/modules/pages/menu_page.py +++ b/ports/stm32/boards/Passport/modules/pages/menu_page.py @@ -23,7 +23,8 @@ def __init__(self, card_header=None, statusbar=None, left_micron=None, - right_micron=None): + right_micron=None, + item_vertical_padding=None): super().__init__(card_header=card_header, statusbar=statusbar, left_micron=left_micron, @@ -35,6 +36,7 @@ def __init__(self, self.focus_idx = focus_idx self.is_top_level = is_top_level self.context = context + self.item_vertical_padding = item_vertical_padding with Stylize(self) as default: default.flex_fill() @@ -67,7 +69,8 @@ def mount(self, lvgl_parent): is_toggle=item_desc.get('is_toggle'), value=item_desc.get('value'), desc=item_desc, - context=self.context) + context=self.context, + vertical_padding=self.item_vertical_padding) is_visible = item_desc.get('is_visible') if is_visible is None or (callable(is_visible) and is_visible()): self.visible_items.append(item) diff --git a/ports/stm32/boards/Passport/modules/views/menu_item.py b/ports/stm32/boards/Passport/modules/views/menu_item.py index cb75ccd5f..64e447585 100644 --- a/ports/stm32/boards/Passport/modules/views/menu_item.py +++ b/ports/stm32/boards/Passport/modules/views/menu_item.py @@ -14,7 +14,8 @@ class MenuItem(View): - def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, context=None): + def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, context=None, + vertical_padding=None): from views import Switch super().__init__(flex_flow=lv.FLEX_FLOW.ROW) @@ -24,13 +25,17 @@ def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, c self.value = value self.desc = desc self.context = context + self.vertical_padding = (vertical_padding if vertical_padding is not None else 10) # Default style with Stylize(self) as default: default.bg_transparent() default.text_color(NORMAL_TEXT) right_pad = 8 if self.is_toggle else 0 - default.pad(top=10, right=right_pad, bottom=10, left=10) + default.pad(top=self.vertical_padding, + right=right_pad, + bottom=self.vertical_padding, + left=10) default.flex_align(track=lv.FLEX_ALIGN.CENTER, cross=lv.FLEX_ALIGN.CENTER) default.img_recolor(NORMAL_TEXT) From f6f67e92c56394743d6736e01a971808606f7d8e Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 16:22:45 -0400 Subject: [PATCH 20/28] SFT-1728: another iteration of hourglass icons --- .../Passport/images/color/ICON_HOURGLASS.c | 31 ++++++------- .../ICON_HOURGLASS__CF_INDEXED_2_BIT.png | Bin 1365 -> 4736 bytes .../Passport/images/mono/ICON_HOURGLASS.c | 39 +++++++++-------- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 41 ++++++++++-------- ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 3998 -> 197 bytes .../mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png | Bin 5944 -> 220 bytes 6 files changed, 58 insertions(+), 53 deletions(-) diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c index 536133f08..c5d1722b5 100644 --- a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c +++ b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c @@ -11,33 +11,34 @@ #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 0x55, 0x55, 0x50, - 0x01, 0x01, 0x55, 0x40, 0x40, - 0x01, 0x00, 0x14, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x01, 0x00, - 0x00, 0x10, 0x14, 0x04, 0x00, - 0x00, 0x05, 0x14, 0x10, 0x00, - 0x00, 0x01, 0x01, 0x40, 0x00, - 0x00, 0x00, 0x41, 0x00, 0x00, - 0x00, 0x00, 0x41, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x40, 0x00, - 0x00, 0x04, 0x00, 0x10, 0x00, - 0x00, 0x10, 0x14, 0x05, 0x00, - 0x01, 0x40, 0x55, 0x01, 0x00, - 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x15, 0x55, 0x54, 0x40, + 0x01, 0x05, 0x55, 0x50, 0x40, + 0x01, 0x41, 0x55, 0x41, 0x40, + 0x00, 0x50, 0x00, 0x05, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, + 0x00, 0x05, 0x00, 0x50, 0x00, + 0x00, 0x01, 0x41, 0x40, 0x00, + 0x00, 0x01, 0x41, 0x40, 0x00, + 0x00, 0x05, 0x00, 0x50, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, + 0x00, 0x50, 0x55, 0x05, 0x00, + 0x01, 0x41, 0x55, 0x41, 0x40, 0x01, 0x05, 0x55, 0x50, 0x40, + 0x01, 0x15, 0x55, 0x54, 0x40, 0x05, 0x55, 0x55, 0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png index 67d9bc759ecb7efd85497c7d675e9d34561a01f3..763d3acd459739d388aeeec1d604635517eafb2d 100644 GIT binary patch literal 4736 zcmeHLc~leU7N4N92r7bDYDLFr-Egw+FHsO8LtWWPjy&(wdBMT2zfa?IqN$CN8i^6guA;{(Bd}SP|K+|BO$)F*UahRNE#9`b@ zXduXXt6{^`L$zbQvhT)AHzdj2mp@Kouh&{<%VR#y&iA=Dyw0KV&Dk)m}eOC2|(T^9rJo4+%2GYYO z4VIU<&r1wwHHXdYaiGL~UxwOxdiRkp*KNpcyxG)vz;^YPBDGbt z(XBr77Ou{I#+7bk44rsr*k|#5&mPBeGgk~7#q`e`UEg2R>&}nj;GnuQ|2@GH*Oy&; z{qwWs=VP`tH{4$z`v|AfgCnQ49Fye!w2KwehCh1tefi~vk-L;PW)@C(UiWRYP+M7e z`kzs|xO*y;nm(!j$o3EUI(_CXucC>kH0Q%t$0P>t(^+cQjKx*=>IdwZx;3(5Twu&` z{}y+z*vc94nr&H$PsV1=8oXTet(thSC2rKK(F4n zFWlOE&~F*9MAfTgW7vWFHqkkl;v`=z~|(Zg0(>- zXJ>3J6c>5MC%Cxx&yE#@xVUV|P7Geo!+6x5S17yfmSngtI9*v>Dl0FSB|Q{pmj_O| zx^8{-;HuSSUL%w~uWVUSl)e6bzf=@ref<2s`7XbvhC5f5r@FE(Z%4}LfxatCYw|V~ ztuV3W}J^RWpj!AfBGmr5<98`R-!6i$PT8HPH%$779Yu((eIJjWW-s#at z>$%sj9G8`~R4WcYL5fUO89T_+AvxrY`(fiJS15k*ng8pp=%VM6X4BM!xRM?Hc2^gc zudQJmSvhF>#UVbN)xH6h(5jFT-bERZXTICvi(@7YdE|N+VW%!(*=kDnR>Q8DP4Lf= z{Zcc&D$Kgw_F&++@hK16Ceysgp?R&B8Ul{;e#)*Nk^1~p{~Gy$)}-hgO{Hnqnpto5 zJ-qtcz!OXKTN9o+FN?#%MT^9%7H5=D0!_PgZyV=lLl9*N5fTz53ki9zf1rBX!Znic zvY^3dW<~Fy`mIUzPB|OZt2FcVl5MeD-Tfx+tNJeM+>TjQ+lR?gQU=WM?7L`OY`_eg z#bWg9S=!HA>o!XI*_fuq@~zTUZWV8>X)jWm`+Cp$a%X%1mcFxA!y4jybyEzpaRu~; z_bKy-g^4y=_!q-<2)A*jpR@BEGTa2@2YGj&*2p==c{1; zn|q5JPbLKNna@Y|!h)Wze7L}il99M&rp5LGtJqTjz0=;CY-qW_O?tBS<(w%i2UGr2 zYd>R|68$RuLco@`bGh>3VJ$@O;=zrJCf!{}nOGFmoL`$@9=fYv(25h2Z$oZN?Z(ZA zu1o6fT&t@s(dNH>3!R@~I&V3J90rYQ5CIxeoIFB=8FVyMZBXGftIh}-76geWSdAz) z7bjsAt|jym>Wh;lRG3gpsMEM|hTIr}&nCj>oABuQkxFd-Tui8@P8i=?Y!v|j9ZsUK zRhO(ci>wl=6ITT89cDTecDj&rCDb^16dYnO;V_5Bp)nAtl}Ke#$M=TCCbdSS2%Xdc z0iGn(*(7Nc(dlVvX|yyp&0x~fnL?qE&S245EChHU=6QM&wIX`+a0f&?MksE^OoWjn z40_msiK+}1QbMJIark|HI-^|P39mPIumJd=TTvsONn_A;I(pX#Gbv34ARP()WrSG? z`Z`^Kn++BdhD%d%JvqE9gc|FNH(E@|&T`Zk9Z$w}z|{<9Wqveem`onk8R4KnOX!Tw zC_wf{o+P39K-NdOIYyl2bWH?=cjA8J{l0dmGw_njMWF`F;z&;xDxo^&7pV;xp%yuB zF(Izv@Kt<7qY>Z;htJR;DyBw&;7pt&V5-z8gMoH|lIhJPs>g5#6ac3Y07r05MP-Hph`5kYgP^@HGtyqm?{ASVrt1jA0*icqY55;K~Lw{rmE+J~^&s9uYM{oNs`_jckh7K_2*2~;YT2H^<#T!bS) zQA7X^1m&Gr4Vi|T@L(;d6jTEQCmMOfVg%WXfL2!(L38!2WKFI zzw_u=i@$RU81A0rllbkXtDCM*V&IdUyVccA*C#RXNzUEs`oGcD`@;tht_N>HY2b5a z!Lb!4@G0aP6`_>g_#?ipt?dQZV+ycPNJV5c6!hc<_~QnmsHTL4Ld$vugo6hUV|ct7 zf;@d4Ck4v4`2(XHDU(axp1Jk+4g6v?`;`YUiDjX|O84tnQo2iPo16dXHZH@xjY!IU zbJK1wtF+tyRc^Oemf7us9+|<(x&D{6t_KF?PR|{ZJ99JyE!*#1fIJlF01hORMuzU6 Hn3VY^%j5ka delta 1265 zcmVrM`$U1_a3H!INBYeR{C38q>o=eUVS1PG+#lYinmDN&At34jlIY-a>a8EE)f?keC ztEYX1Uf*80)}ZZKAN-tQKOLPDmqVE=GY0)JNa*>{Ux9?OAA?-n9z%cK_~_devA^A7 z%k}MKK8Hs|GGwVMtb}F3E`DSlX3?uht#!sK%GG09_eA7ekhuBMEP=Ch(= zxUw_ff#HUf%X%$C47Yz`Y)cBnV~q`PdTeonG%CcPGEk7zsnVc*^^F=rOuVz9XKpM@ z=VDyA$>fx#&|s4yT^uYM?*ZtN@Er@Sb<0|>kz?gfFlB->!YehH zemX#ku(l!?lzpO@u?HwqG-e}09vy%xBF8@A4FpJ)JA&jSg7e7Od9ICjjFv4cXP#=D zO#q>iv9}zADqy9gs2@v?8mcOqR5h!q2Q6B&EX>4Tx0C=2zkv&MmKp2MK zrb$B!1+& z;_(~jqRRr$jGCGBJaLp*EOxNc!K`Fz#8bpERnsY7$a<`D-r}rPYOHln{=#rxUs>il ztsx|_h$To6p`eB`DzFi!T_?ptn$8nG{z2C-kxL<08H^kYs6vD6`oaI;cWQ(9nI5-4Gifa9= z`f`NXFOS&(000J1OjJeSf>r +// SPDX-FileCopyrightText: © 2022 Foundation Devices, Inc. // SPDX-License-Identifier: GPL-3.0-or-later // @@ -7,35 +7,36 @@ #else #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS #endif + const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0xff, 0x80, - 0x17, 0xfd, 0x00, - 0x13, 0xf9, 0x00, - 0x09, 0xf2, 0x00, - 0x04, 0x04, 0x00, - 0x02, 0x48, 0x00, - 0x01, 0x10, 0x00, - 0x00, 0xa0, 0x00, - 0x00, 0xa0, 0x00, - 0x01, 0x10, 0x00, - 0x02, 0x48, 0x00, - 0x04, 0xe4, 0x00, - 0x09, 0xf2, 0x00, - 0x13, 0xf9, 0x00, - 0x17, 0xfd, 0x00, - 0x3f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x17, 0xfe, 0x80, + 0x13, 0xfc, 0x80, + 0x19, 0xf9, 0x80, + 0x0c, 0x03, 0x00, + 0x06, 0x66, 0x00, + 0x03, 0x0c, 0x00, + 0x01, 0x98, 0x00, + 0x01, 0x98, 0x00, + 0x03, 0x0c, 0x00, + 0x06, 0x66, 0x00, + 0x0c, 0xf3, 0x00, + 0x19, 0xf9, 0x80, + 0x13, 0xfc, 0x80, + 0x17, 0xfe, 0x80, + 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index d6bf6ecb9..cf159820f 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -8,35 +8,38 @@ #include "lvgl/lvgl.h" #endif + #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND #endif + const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, - 0x3f, 0xff, 0x80, - 0x7f, 0xff, 0xc0, - 0x7f, 0xff, 0xc0, - 0x3f, 0xff, 0x80, - 0x1f, 0xff, 0x00, - 0x0f, 0xfe, 0x00, - 0x07, 0xfc, 0x00, - 0x03, 0xf8, 0x00, - 0x01, 0xf0, 0x00, - 0x01, 0xf0, 0x00, - 0x03, 0xf8, 0x00, - 0x07, 0xfc, 0x00, - 0x0f, 0xfe, 0x00, - 0x1f, 0xff, 0x00, - 0x3f, 0xff, 0x80, - 0x7f, 0xff, 0xc0, - 0x7f, 0xff, 0xc0, - 0x3f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x7f, 0xff, 0xe0, + 0x7f, 0xff, 0xe0, + 0x3f, 0xff, 0xc0, + 0x3f, 0xff, 0xc0, + 0x1f, 0xff, 0x80, + 0x0f, 0xff, 0x00, + 0x07, 0xfe, 0x00, + 0x03, 0xfc, 0x00, + 0x03, 0xfc, 0x00, + 0x07, 0xfe, 0x00, + 0x0f, 0xff, 0x00, + 0x1f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x3f, 0xff, 0xc0, + 0x7f, 0xff, 0xe0, + 0x7f, 0xff, 0xe0, + 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index e501dd4f0a5c02e3384ec3d86a1ce932641671eb..c3e7aaca68019863ea22190160b9de26f9ad2ec3 100644 GIT binary patch delta 181 zcmbOyf0S{8L_G%^0|P^Erz|g!;w(0r%1aer? z9eo`c7&i8E|4C#8@}oRm978G?pI-6iVo>044b1*u{m$5636Ix&ffDy#J~w%#f==mCjY#dG3h*xXKl$^fc+!!}|d;F>R c{k(g)tge|z%&dQPA7~AOr>mdKI;Vst0N*$|Y5)KL delta 3991 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 BN{#>k literal 5944 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 aMgjoGoe*gOApijY0000 Date: Wed, 17 Jul 2024 20:38:32 -0400 Subject: [PATCH 21/28] SFT-1728: improved mono background for hourglass icon --- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 45 +++++++++--------- ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 197 -> 198 bytes 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index cf159820f..24af5db38 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -8,7 +8,6 @@ #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif @@ -22,24 +21,26 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, - 0x3f, 0xff, 0xc0, - 0x7f, 0xff, 0xe0, - 0x7f, 0xff, 0xe0, - 0x3f, 0xff, 0xc0, - 0x3f, 0xff, 0xc0, - 0x1f, 0xff, 0x80, - 0x0f, 0xff, 0x00, - 0x07, 0xfe, 0x00, - 0x03, 0xfc, 0x00, - 0x03, 0xfc, 0x00, - 0x07, 0xfe, 0x00, - 0x0f, 0xff, 0x00, - 0x1f, 0xff, 0x80, - 0x3f, 0xff, 0xc0, - 0x3f, 0xff, 0xc0, - 0x7f, 0xff, 0xe0, - 0x7f, 0xff, 0xe0, - 0x3f, 0xff, 0xc0, + 0x00, 0x00, 0x00, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x0f, 0xff, 0xc0, + 0x07, 0xff, 0x80, + 0x03, 0xff, 0x00, + 0x01, 0xfe, 0x00, + 0x01, 0xfe, 0x00, + 0x03, 0xff, 0x00, + 0x07, 0xff, 0x80, + 0x0f, 0xff, 0xc0, + 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; @@ -47,8 +48,8 @@ const lv_img_dsc_t ICON_HOURGLASS_BACKGROUND = { .header.cf = LV_IMG_CF_INDEXED_1BIT, .header.always_zero = 0, .header.reserved = 0, - .header.w = 20, - .header.h = 20, - .data_size = 68, + .header.w = 22, + .header.h = 22, + .data_size = 74, .data = ICON_HOURGLASS_BACKGROUND_map, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index c3e7aaca68019863ea22190160b9de26f9ad2ec3..e8483790652d744ba77c5982a7fe19acfcb11342 100644 GIT binary patch delta 154 zcmX@gc#Ki8Gr-TCmrII^fq{Y7)59eQNQ;3m2OE$)vL(lQqM~U*w5N+>NCo5DE8biT z3LLJ1+5fBG8FMT$@v3aPFxS-5!+uf5(#pW76pgKBH@enUs0D0T`(fse<#Vg%O=~s% z(be^V``kkJA8gYv%)9@vS0S3w(&j{I_t#JV-ixj0`Z(7_qH~Y*0-$vap00i_>zopr E0A4~nSO5S3 delta 153 zcmX@cc$87GGr-TCmrII^fq{Y7)59eQNQ;0l2OE&=?Ud!6sA!rW<>}%WQo;E2iZ>U7 z0*7m0_W$a4#s*7xyygp(xcBn8$tx9fiZ6BT3(9VY^I0_Wz`^Y@=85O6RQ Date: Wed, 17 Jul 2024 22:08:11 -0400 Subject: [PATCH 22/28] SFT-1728: manually edited hourglass background code to increase its visual buffer --- .../Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index 24af5db38..332122dd4 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -22,24 +22,24 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xff, 0xe0, 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xc0, 0x07, 0xff, 0x80, 0x03, 0xff, 0x00, - 0x01, 0xfe, 0x00, - 0x01, 0xfe, 0x00, 0x03, 0xff, 0x00, 0x07, 0xff, 0x80, 0x0f, 0xff, 0xc0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, - 0x1f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; From 8c769841f70ef4b7861acf1ba82af704992daf46 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 22 Jul 2024 00:44:12 -0400 Subject: [PATCH 23/28] SFT-1728: finalized hourglass mono background --- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 3 +-- ...N_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 198 -> 192 bytes 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index 332122dd4..d30d9339a 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -7,7 +7,6 @@ #else #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif @@ -18,7 +17,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index e8483790652d744ba77c5982a7fe19acfcb11342..dcffdda85ec154ca9d88ff7b1043e701f8f346eb 100644 GIT binary patch delta 113 zcmV-%0FM900l)!}R%%sAL_t(|0qvBr3IHGo1-1YG=jsK4gbi5+g11!54Hrrj4W5_U zp$BjWXCWut#4I1TIc5eBJO!}kkQr35MZQY6;uEXEXr*~k zT4X4-HrR@;cqUM=t?-$+r=5D9mW4I=ii+?H&Y?&62A9yD-{FlQTLC#d$aRbL^8W7@ ZuL|* Date: Mon, 22 Jul 2024 01:10:43 -0400 Subject: [PATCH 24/28] SFT-1728: ensured initial seeds can be temporary, removed print --- .../boards/Passport/modules/flows/restore_seed_flow.py | 9 ++++++--- .../Passport/modules/pages/predictive_text_input_page.py | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index c94d3472b..6f65e8400 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -49,10 +49,12 @@ async def choose_temporary(self): self.set_result(False) return - if permanent: - self.goto(self.choose_restore_method) - else: + self.temporary = not permanent + if self.temporary: + settings.enter_temporary_mode() self.goto(self.explain_temporary) + else: + self.goto(self.choose_restore_method) async def explain_temporary(self): from pages import InfoPage @@ -66,6 +68,7 @@ async def explain_temporary(self): if not result: if not self.back(): + settings.exit_temporary_mode() self.set_result(None) return diff --git a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py index 84c1c4e67..c50dbb8a6 100644 --- a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py +++ b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py @@ -97,7 +97,6 @@ def update_predictions(self): # print('Lookup words for {}'.format(prefix)) set_list(self.prefixes, self.word_idx, prefix) self.predictions = get_words_matching_prefix(prefix, max=10, word_list=self.word_list) - print("len(predictions): {}".format(len(self.predictions))) elif self.word_idx == self.total_words - 1: self.predictions = [RANDOM_WORD_STRING] else: From 77bfe63ad570ac96b1406861f6308ef6ab854cb9 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 23 Jul 2024 13:00:10 -0400 Subject: [PATCH 25/28] SFT-1728: fixed import header and added cancel button to initial backup prompt --- .../boards/Passport/modules/flows/backup_flow.py | 5 +++-- .../Passport/modules/flows/restore_seed_flow.py | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/backup_flow.py b/ports/stm32/boards/Passport/modules/flows/backup_flow.py index b038b65df..42cd03c54 100644 --- a/ports/stm32/boards/Passport/modules/flows/backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/backup_flow.py @@ -13,7 +13,7 @@ class BackupFlow(Flow): - def __init__(self, initial_backup=False): + def __init__(self, initial_backup=False, left_micron=microns.Back): from common import settings super().__init__(initial_state=self.show_intro, name='BackupFlow') self.backup_quiz_passed = settings.get('backup_quiz', False) @@ -21,6 +21,7 @@ def __init__(self, initial_backup=False): self.statusbar = {'title': 'BACKUP', 'icon': 'ICON_BACKUP'} self.initial_backup = initial_backup + self.first_left_micron = left_micron async def show_intro(self): from pages import InfoPage @@ -42,7 +43,7 @@ async def show_intro(self): result = await InfoPage( icon=lv.LARGE_ICON_BACKUP, text=msgs, - left_micron=microns.Back, + left_micron=self.first_left_micron, right_micron=microns.Forward).show() if result: diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 6f65e8400..6a84b9565 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -17,6 +17,12 @@ class RestoreSeedFlow(Flow): def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False, temporary=False): + initial_state = self.choose_temporary + if temporary: + initial_state = self.explain_temporary + + super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + self.refresh_cards_when_done = refresh_cards_when_done self.seed_format = None self.seed_length = None @@ -25,14 +31,10 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.seed_words = [] self.full_backup = full_backup self.autobackup = autobackup - self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} self.temporary = temporary - initial_state = self.choose_temporary - if self.temporary: - initial_state = self.explain_temporary - - super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + # This must be after super().__init__, so it isn't set to null + self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} async def choose_temporary(self): from pages import ChooserPage @@ -228,7 +230,7 @@ async def valid_seed(self): text = 'New seed imported and {}'.format('applied' if self.temporary else 'saved') await SuccessPage(text=text).show() if self.full_backup: - await BackupFlow(initial_backup=True).run() + await BackupFlow(initial_backup=True, left_micron=microns.Cancel).run() elif self.autobackup: await AutoBackupFlow(offer=True).run() From 81ef57d9a58a1113b9176f51189f45e65938d146 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 23 Jul 2024 13:41:11 -0400 Subject: [PATCH 26/28] SFT-1728: removed unnecessary changes, fixed some copy, used hourglass indicator for active temporary seeds --- .../Passport/modules/flows/temporary_seed_flow.py | 2 +- .../Passport/modules/tasks/restore_backup_task.py | 13 +++++-------- ports/stm32/boards/Passport/modules/ui/ui.py | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 3ed73f720..643c6ca7d 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -83,7 +83,7 @@ async def use_child_seed(self): self.set_result(None) return - await SuccessPage(text='Temporary Seed Applied.').show() + await SuccessPage(text='Temporary Seed Applied').show() self.set_result(True) ui.full_cards_refresh() diff --git a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py index 7cded7282..f2a6afc10 100644 --- a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py @@ -107,14 +107,11 @@ async def restore_backup_task(on_done, decryption_password, backup_file_path): await on_done(Error.CORRUPT_BACKUP_FILE) return - if settings.temporary_mode: - settings.set_volatile('temporary_seed', raw) - else: - # Set the secret into the Secure Element - pa.change(new_secret=raw) - - # Force the right chain and update XFP & XPUB - await pa.new_main_secret(raw, chain) + # Set the secret into the Secure Element + pa.change(new_secret=raw) + + # Force the right chain and update XFP & XPUB + await pa.new_main_secret(raw, chain) # Finally, restore the settings for idx, k in enumerate(vals): diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index e066b758b..ed566121f 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -255,7 +255,7 @@ def update_cards(self, # print('account[{}]={}'.format(account, i)) account_card = { - 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_SEED', + 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_HOURGLASS', 'header_color': LIGHT_GREY, 'header_fg_color': LIGHT_TEXT, 'statusbar': {'title': 'ACCOUNT', 'icon': 'ICON_FOLDER', 'fg_color': get_account_fg(account)}, From ed8702a09a6f83170a43ac43e2fb85ce11325331 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 26 Jul 2024 12:45:04 -0400 Subject: [PATCH 27/28] SFT-1728: fixed account index out of bounds when switching keys --- .../Passport/modules/flows/temporary_seed_flow.py | 9 ++++----- ports/stm32/boards/Passport/modules/ui/ui.py | 10 ++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 643c6ca7d..dea360732 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -41,10 +41,7 @@ async def clear_seed(self): settings.exit_temporary_mode() # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary Seed Cleared').show() - - self.set_result(True) - ui.full_cards_refresh() - await self.wait_to_die() + await self.finalize() async def use_child_seed(self): from derived_key import get_key_type_from_tn @@ -84,7 +81,9 @@ async def use_child_seed(self): return await SuccessPage(text='Temporary Seed Applied').show() + await self.finalize() + async def finalize(self): self.set_result(True) - ui.full_cards_refresh() + ui.full_cards_refresh(go_to_account_0=True) await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index ed566121f..68f045d4d 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -98,8 +98,10 @@ def set_screen_bg_color(self, bg_color): self.active_screen.set_bg_color(bg_color) def set_cards(self, card_descs, active_idx=0): - assert(active_idx >= 0) - assert(active_idx < len(card_descs)) + # An index out of bounds could occur when the account/passphrase/seed changes + # and the active_idx isnt updated + if active_idx < 0 or active_idx > len(card_descs): + active_idx = 1 # print('set_cards: len={}'.format(len(card_descs))) self.card_descs = card_descs @@ -336,13 +338,13 @@ def on_nav_right(self): self.next_card() # Full refresh - def full_cards_refresh(self): + def full_cards_refresh(self, go_to_account_0=False): from utils import start_task self.update_cards() async def restart_main_task(): - self.start_card_task(card_idx=self.active_card_idx) + self.start_card_task(card_idx=(1 if go_to_account_0 else self.active_card_idx)) start_task(restart_main_task()) From a88b85559013cb7d55d3b908f4180564687454ae Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 30 Jul 2024 10:15:01 -0400 Subject: [PATCH 28/28] SFT-1728: temporary seeds entered from plus menu return to plus menu --- .../boards/Passport/modules/flows/temporary_seed_flow.py | 6 +++--- ports/stm32/boards/Passport/modules/ui/ui.py | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index dea360732..43f547162 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -81,9 +81,9 @@ async def use_child_seed(self): return await SuccessPage(text='Temporary Seed Applied').show() - await self.finalize() + await self.finalize(child=True) - async def finalize(self): + async def finalize(self, child=False): self.set_result(True) - ui.full_cards_refresh(go_to_account_0=True) + ui.full_cards_refresh(go_to_account_0=child, go_to_plus_menu=(not child)) await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index 68f045d4d..f071a4953 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -338,13 +338,14 @@ def on_nav_right(self): self.next_card() # Full refresh - def full_cards_refresh(self, go_to_account_0=False): + def full_cards_refresh(self, go_to_account_0=False, go_to_plus_menu=False): from utils import start_task - self.update_cards() + self.update_cards(is_init=go_to_account_0, + stay_on_last_card=go_to_plus_menu) async def restart_main_task(): - self.start_card_task(card_idx=(1 if go_to_account_0 else self.active_card_idx)) + self.start_card_task(card_idx=self.active_card_idx) start_task(restart_main_task())