diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 49d1f25f9..df0c8b95e 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -217,18 +217,8 @@ def storage(self): return self._storage - def get_seed(self, seed_num: int) -> Seed: - if seed_num < len(self.storage.seeds): - return self.storage.seeds[seed_num] - else: - raise Exception(f"There is no seed_num {seed_num}; only {len(self.storage.seeds)} in memory.") - - - def discard_seed(self, seed_num: int): - if seed_num < len(self.storage.seeds): - del self.storage.seeds[seed_num] - else: - raise Exception(f"There is no seed_num {seed_num}; only {len(self.storage.seeds)} in memory.") + def discard_seed(self, seed: Seed): + self.storage.seeds.remove(seed) def pop_prev_from_back_stack(self): diff --git a/src/seedsigner/models/seed_storage.py b/src/seedsigner/models/seed_storage.py index 85feb49cb..09224d3cf 100644 --- a/src/seedsigner/models/seed_storage.py +++ b/src/seedsigner/models/seed_storage.py @@ -20,15 +20,13 @@ def get_pending_seed(self) -> Seed: return self.pending_seed - def finalize_pending_seed(self) -> int: - # Finally store the pending seed and return its index - if self.pending_seed in self.seeds: - index = self.seeds.index(self.pending_seed) - else: - self.seeds.append(self.pending_seed) - index = len(self.seeds) - 1 + def finalize_pending_seed(self) -> Seed: + # Store the pending seed and return it + seed = self.pending_seed + if seed not in self.seeds: + self.seeds.append(seed) self.pending_seed = None - return index + return seed def clear_pending_seed(self): diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 475aef49a..9e0fbeb1c 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -58,7 +58,7 @@ def run(self): if len(seeds) > 0 and selected_menu_num < len(seeds): # User selected one of the n seeds - self.controller.psbt_seed = self.controller.get_seed(selected_menu_num) + self.controller.psbt_seed = seeds[selected_menu_num] return Destination(PSBTOverviewView) # The remaining flows are a sub-flow; resume PSBT flow once the seed is loaded. diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 45881fb5c..93738afb1 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -56,7 +56,8 @@ def run(self): return Destination(BackStackView) elif len(self.seeds) > 0 and selected_menu_num < len(self.seeds): - return Destination(SeedOptionsView, view_args={"seed_num": selected_menu_num}) + selected_seed = self.controller.storage.seeds[selected_menu_num] + return Destination(SeedOptionsView, view_args={"seed": selected_seed}) elif button_data[selected_menu_num] == self.LOAD: return Destination(LoadSeedView) @@ -128,12 +129,12 @@ def run(self): if len(seeds) > 0 and selected_menu_num < len(seeds): # User selected one of the n seeds - view_args = dict(seed_num=selected_menu_num) + seed = seeds[selected_menu_num] if self.flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR: - return Destination(SeedAddressVerificationView, view_args=view_args) + return Destination(SeedAddressVerificationView, view_args={"seed": seed}) elif self.flow == Controller.FLOW__SIGN_MESSAGE: - self.controller.sign_message_data["seed_num"] = selected_menu_num + self.controller.sign_message_data["seed"] = seed return Destination(SeedSignMessageConfirmMessageView) self.controller.resume_main_flow = self.flow @@ -336,8 +337,8 @@ def run(self): ) if button_data[selected_menu_num] == self.FINALIZE: - seed_num = self.controller.storage.finalize_pending_seed() - return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True) + seed = self.controller.storage.finalize_pending_seed() + return Destination(SeedOptionsView, view_args={"seed": seed}, clear_history=True) elif button_data[selected_menu_num] == self.PASSPHRASE: return Destination(SeedAddPassphraseView) @@ -450,8 +451,8 @@ def run(self): return Destination(SeedAddPassphraseView) elif button_data[selected_menu_num] == self.DONE: - seed_num = self.controller.storage.finalize_pending_seed() - return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True) + seed = self.controller.storage.finalize_pending_seed() + return Destination(SeedOptionsView, view_args={"seed": seed}, clear_history=True) @@ -459,13 +460,14 @@ class SeedDiscardView(View): KEEP = ButtonOption("Keep seed") DISCARD = ButtonOption("Discard", button_label_color="red") - def __init__(self, seed_num: int = None): + def __init__(self, seed: Seed = None): super().__init__() - self.seed_num = seed_num - if self.seed_num is not None: - self.seed = self.controller.get_seed(self.seed_num) + if seed is None: + self.is_pending_seed = True + self.seed = self.controller.storage.get_pending_seed() else: - self.seed = self.controller.storage.pending_seed + self.is_pending_seed = False + self.seed = seed def run(self): @@ -485,16 +487,16 @@ def run(self): if button_data[selected_menu_num] == self.KEEP: # Use skip_current_view=True to prevent BACK from landing on this warning screen - if self.seed_num is not None: - return Destination(SeedOptionsView, view_args={"seed_num": self.seed_num}, skip_current_view=True) - else: + if self.is_pending_seed: return Destination(SeedFinalizeView, skip_current_view=True) + else: + return Destination(SeedOptionsView, view_args={"seed": self.seed}, skip_current_view=True) elif button_data[selected_menu_num] == self.DISCARD: - if self.seed_num is not None: - self.controller.discard_seed(self.seed_num) - else: + if self.is_pending_seed: self.controller.storage.clear_pending_seed() + else: + self.controller.discard_seed(self.seed) return Destination(MainMenuView, clear_history=True) @@ -533,10 +535,9 @@ class SeedOptionsView(View): DISCARD = ButtonOption("Discard seed", button_label_color="red") - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num - self.seed = self.controller.get_seed(self.seed_num) + self.seed = seed def run(self): @@ -547,16 +548,16 @@ def run(self): if self.controller.resume_main_flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR: # Jump straight back into the single sig addr verification flow self.controller.resume_main_flow = None - return Destination(SeedAddressVerificationView, view_args=dict(seed_num=self.seed_num), skip_current_view=True) + return Destination(SeedAddressVerificationView, view_args=dict(seed=self.seed), skip_current_view=True) if self.controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER: # Jump straight back into the address explorer script type selection flow # But don't cancel the `resume_main_flow` as we'll still need that after # derivation path is specified. - return Destination(SeedExportXpubScriptTypeView, view_args=dict(seed_num=self.seed_num, sig_type=SettingsConstants.SINGLE_SIG), skip_current_view=True) + return Destination(SeedExportXpubScriptTypeView, view_args=dict(seed=self.seed, sig_type=SettingsConstants.SINGLE_SIG), skip_current_view=True) elif self.controller.resume_main_flow == Controller.FLOW__SIGN_MESSAGE: - self.controller.sign_message_data["seed_num"] = self.seed_num + self.controller.sign_message_data["seed"] = self.seed return Destination(SeedSignMessageConfirmMessageView, skip_current_view=True) if self.controller.psbt: @@ -597,30 +598,30 @@ def run(self): if button_data[selected_menu_num] == self.SCAN_PSBT: from seedsigner.views.scan_views import ScanPSBTView - self.controller.psbt_seed = self.controller.get_seed(self.seed_num) + self.controller.psbt_seed = self.seed return Destination(ScanPSBTView) elif button_data[selected_menu_num] == self.EXPORT_XPUB: - return Destination(SeedExportXpubSigTypeView, view_args=dict(seed_num=self.seed_num)) + return Destination(SeedExportXpubSigTypeView, view_args=dict(seed=self.seed)) elif button_data[selected_menu_num] == self.EXPLORER: self.controller.resume_main_flow = Controller.FLOW__ADDRESS_EXPLORER - return Destination(SeedExportXpubScriptTypeView, view_args=dict(seed_num=self.seed_num, sig_type=SettingsConstants.SINGLE_SIG)) + return Destination(SeedExportXpubScriptTypeView, view_args=dict(seed=self.seed, sig_type=SettingsConstants.SINGLE_SIG)) elif button_data[selected_menu_num] == self.SIGN_MESSAGE: from seedsigner.views.scan_views import ScanView - self.controller.sign_message_data = dict(seed_num=self.seed_num) + self.controller.sign_message_data = dict(seed=self.seed) self.controller.resume_main_flow = Controller.FLOW__SIGN_MESSAGE return Destination(ScanView) elif button_data[selected_menu_num] == self.BACKUP: - return Destination(SeedBackupView, view_args=dict(seed_num=self.seed_num)) + return Destination(SeedBackupView, view_args=dict(seed=self.seed)) elif button_data[selected_menu_num] == self.BIP85_CHILD_SEED: - return Destination(SeedBIP85SelectNumWordsView, view_args={"seed_num": self.seed_num}) + return Destination(SeedBIP85SelectNumWordsView, view_args={"seed": self.seed}) elif button_data[selected_menu_num] == self.DISCARD: - return Destination(SeedDiscardView, view_args=dict(seed_num=self.seed_num)) + return Destination(SeedDiscardView, view_args=dict(seed=self.seed)) @@ -628,10 +629,9 @@ class SeedBackupView(View): VIEW_WORDS = ButtonOption("View seed words") EXPORT_SEEDQR = ButtonOption("Export as SeedQR") - def __init__(self, seed_num): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num - self.seed = self.controller.get_seed(self.seed_num) + self.seed = seed def run(self): @@ -651,10 +651,10 @@ def run(self): return Destination(BackStackView) elif button_data[selected_menu_num] == self.VIEW_WORDS: - return Destination(SeedWordsWarningView, view_args={"seed_num": self.seed_num}) + return Destination(SeedWordsWarningView, view_args={"seed": self.seed}) elif button_data[selected_menu_num] == self.EXPORT_SEEDQR: - return Destination(SeedTranscribeSeedQRFormatView, view_args={"seed_num": self.seed_num}) + return Destination(SeedTranscribeSeedQRFormatView, view_args={"seed": self.seed}) @@ -665,15 +665,15 @@ class SeedExportXpubSigTypeView(View): SINGLE_SIG = ButtonOption("Single Sig", return_data=SettingsConstants.SINGLE_SIG) MULTISIG = ButtonOption("Multisig", return_data=SettingsConstants.MULTISIG) - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num + self.seed = seed def run(self): if len(self.settings.get_value(SettingsConstants.SETTING__SIG_TYPES)) == 1: # Nothing to select; skip this screen - return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": self.settings.get_value(SettingsConstants.SETTING__SIG_TYPES)[0]}, skip_current_view=True) + return Destination(SeedExportXpubScriptTypeView, view_args={"seed": self.seed, "sig_type": self.settings.get_value(SettingsConstants.SETTING__SIG_TYPES)[0]}, skip_current_view=True) button_data = [self.SINGLE_SIG, self.MULTISIG] @@ -686,30 +686,29 @@ def run(self): if selected_menu_num == RET_CODE__BACK_BUTTON: return Destination(BackStackView) - return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": button_data[selected_menu_num].return_data}) + return Destination(SeedExportXpubScriptTypeView, view_args={"seed": self.seed, "sig_type": button_data[selected_menu_num].return_data}) class SeedExportXpubScriptTypeView(View): - def __init__(self, seed_num: int, sig_type: str): + def __init__(self, seed: Seed, sig_type: str): super().__init__() - self.seed_num = seed_num + self.seed = seed self.sig_type = sig_type def run(self): from seedsigner.controller import Controller from .tools_views import ToolsAddressExplorerAddressTypeView - args = {"seed_num": self.seed_num, "sig_type": self.sig_type} + args = {"seed": self.seed, "sig_type": self.sig_type} script_types = self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES) - seed = self.controller.storage.seeds[self.seed_num] - if seed.script_override: + if self.seed.script_override: # This seed only allows one script type # TODO: Does it matter if the Settings don't have the override script type # enabled? - script_types = [seed.script_override] + script_types = [self.seed.script_override] if len(script_types) == 1: # Nothing to select; skip this screen @@ -759,9 +758,9 @@ def run(self): class SeedExportXpubCustomDerivationView(View): - def __init__(self, seed_num: int, sig_type: str, script_type: str): + def __init__(self, seed: Seed, sig_type: str, script_type: str): super().__init__() - self.seed_num = seed_num + self.seed = seed self.sig_type = sig_type self.script_type = script_type self.custom_derivation_path = "m/" @@ -782,12 +781,12 @@ def run(self): if self.controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER: from .tools_views import ToolsAddressExplorerAddressTypeView - return Destination(ToolsAddressExplorerAddressTypeView, view_args=dict(seed_num=self.seed_num, script_type=self.script_type, custom_derivation=custom_derivation)) + return Destination(ToolsAddressExplorerAddressTypeView, view_args=dict(seed=self.seed, script_type=self.script_type, custom_derivation=custom_derivation)) return Destination( SeedExportXpubQRFormatView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "sig_type": self.sig_type, "script_type": self.script_type, "custom_derivation": custom_derivation, @@ -797,9 +796,9 @@ def run(self): class SeedExportXpubQRFormatView(View): - def __init__(self, seed_num: int, sig_type: str, script_type: str, custom_derivation: str = None): + def __init__(self, seed: Seed, sig_type: str, script_type: str, custom_derivation: str = None): super().__init__() - self.seed_num = seed_num + self.seed = seed self.sig_type = sig_type self.script_type = script_type self.custom_derivation = custom_derivation @@ -807,7 +806,7 @@ def __init__(self, seed_num: int, sig_type: str, script_type: str, custom_deriva def run(self): args = { - "seed_num": self.seed_num, + "seed": self.seed, "sig_type": self.sig_type, "script_type": self.script_type, "custom_derivation": self.custom_derivation, @@ -839,9 +838,9 @@ def run(self): class SeedExportXpubWarningView(View): - def __init__(self, seed_num: int, sig_type: str, script_type: str, xpub_qr_format: str, custom_derivation: str): + def __init__(self, seed: Seed, sig_type: str, script_type: str, xpub_qr_format: str, custom_derivation: str): super().__init__() - self.seed_num = seed_num + self.seed = seed self.sig_type = sig_type self.script_type = script_type self.xpub_qr_format = xpub_qr_format @@ -852,7 +851,7 @@ def run(self): destination = Destination( SeedExportXpubDetailsView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "sig_type": self.sig_type, "script_type": self.script_type, "xpub_qr_format": self.xpub_qr_format, @@ -885,15 +884,14 @@ class SeedExportXpubDetailsView(View): Collects the user input from all the previous screens leading up to this and finally calculates the xpub and displays the summary view to the user. """ - def __init__(self, seed_num: int, sig_type: str, script_type: str, xpub_qr_format: str, custom_derivation: str): + def __init__(self, seed: Seed, sig_type: str, script_type: str, xpub_qr_format: str, custom_derivation: str): super().__init__() self.sig_type = sig_type self.script_type = script_type self.xpub_qr_format = xpub_qr_format self.custom_derivation = custom_derivation - self.seed_num = seed_num - self.seed = self.controller.get_seed(self.seed_num) + self.seed = seed def run(self): @@ -951,7 +949,7 @@ def run(self): if selected_menu_num == 0: return Destination( SeedExportXpubQRDisplayView, - dict(seed_num=self.seed_num, + dict(seed=self.seed, xpub_qr_format=self.xpub_qr_format, derivation_path=derivation_path, sig_type=self.sig_type @@ -964,9 +962,9 @@ def run(self): class SeedExportXpubQRDisplayView(View): - def __init__(self, seed_num: int, xpub_qr_format: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG): + def __init__(self, seed: Seed, xpub_qr_format: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG): super().__init__() - self.seed = self.controller.get_seed(seed_num) + self.seed = seed encoder_args = dict( seed=self.seed, @@ -1002,9 +1000,9 @@ def run(self): View Seed Words flow ****************************************************************************""" class SeedWordsWarningView(View): - def __init__(self, seed_num: int, bip85_data: dict = None): + def __init__(self, seed: Seed, bip85_data: dict = None): super().__init__() - self.seed_num = seed_num + self.seed = seed self.bip85_data = bip85_data @@ -1012,7 +1010,7 @@ def run(self): destination = Destination( SeedWordsView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, page_index=0, bip85_data=self.bip85_data ), @@ -1040,13 +1038,14 @@ class SeedWordsView(View): NEXT = ButtonOption("Next") DONE = ButtonOption("Done") - def __init__(self, seed_num: int, bip85_data: dict = None, page_index: int = 0): + def __init__(self, seed: Seed, bip85_data: dict = None, page_index: int = 0): super().__init__() - self.seed_num = seed_num - if self.seed_num is None: + if seed is None: + self.is_pending_seed = True self.seed = self.controller.storage.get_pending_seed() else: - self.seed = self.controller.get_seed(self.seed_num) + self.is_pending_seed = False + self.seed = seed self.bip85_data = bip85_data self.page_index = page_index @@ -1066,7 +1065,7 @@ def run(self): button_data = [] num_pages = int(len(mnemonic)/words_per_page) - if self.page_index < num_pages - 1 or self.seed_num is None: + if self.page_index < num_pages - 1 or self.is_pending_seed: button_data.append(self.NEXT) else: button_data.append(self.DONE) @@ -1083,23 +1082,26 @@ def run(self): if selected_menu_num == RET_CODE__BACK_BUTTON: return Destination(BackStackView) + if self.is_pending_seed: + self.seed = None # Set to None for next View to know it's a pending seed + if button_data[selected_menu_num] == self.NEXT: - if self.seed_num is None and self.page_index == num_pages - 1: + if self.is_pending_seed and self.page_index == num_pages - 1: return Destination( SeedWordsBackupTestPromptView, - view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data), + view_args=dict(seed=self.seed, bip85_data=self.bip85_data), ) else: return Destination( SeedWordsView, - view_args=dict(seed_num=self.seed_num, page_index=self.page_index + 1, bip85_data=self.bip85_data) + view_args=dict(seed=self.seed, page_index=self.page_index + 1, bip85_data=self.bip85_data) ) elif button_data[selected_menu_num] == self.DONE: # Must clear history to avoid BACK button returning to private info return Destination( SeedWordsBackupTestPromptView, - view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data), + view_args=dict(seed=self.seed, bip85_data=self.bip85_data), ) @@ -1111,9 +1113,9 @@ class SeedBIP85SelectNumWordsView(View): WORDS_12 = ButtonOption("12 Words") WORDS_24 = ButtonOption("24 Words") - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num + self.seed = seed self.num_words = 0 @@ -1136,16 +1138,16 @@ def run(self): return Destination( SeedBIP85SelectChildIndexView, - view_args=dict(seed_num=self.seed_num, num_words=self.num_words) + view_args=dict(seed=self.seed, num_words=self.num_words) ) class SeedBIP85SelectChildIndexView(View): # View to retrieve the derived seed index - def __init__(self, seed_num: int, num_words: int): + def __init__(self, seed: Seed, num_words: int): super().__init__() - self.seed_num = seed_num + self.seed = seed self.num_words = num_words @@ -1160,7 +1162,7 @@ def run(self): return Destination( SeedBIP85InvalidChildIndexView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, num_words=self.num_words ), skip_current_view=True @@ -1169,7 +1171,7 @@ def run(self): return Destination( SeedWordsWarningView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, bip85_data=dict(child_index=int(ret), num_words=self.num_words), ) ) @@ -1177,9 +1179,9 @@ def run(self): class SeedBIP85InvalidChildIndexView(View): - def __init__(self, seed_num: int, num_words: int): + def __init__(self, seed: Seed, num_words: int): super().__init__() - self.seed_num = seed_num + self.seed = seed self.num_words = num_words @@ -1197,7 +1199,7 @@ def run(self): return Destination( SeedBIP85SelectChildIndexView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, num_words=self.num_words ), skip_current_view=True @@ -1212,9 +1214,9 @@ class SeedWordsBackupTestPromptView(View): VERIFY = ButtonOption("Verify") SKIP = ButtonOption("Skip") - def __init__(self, seed_num: int, bip85_data: dict = None): + def __init__(self, seed: Seed, bip85_data: dict = None): super().__init__() - self.seed_num = seed_num + self.seed = seed self.bip85_data = bip85_data @@ -1228,29 +1230,30 @@ def run(self): if button_data[selected_menu_num] == self.VERIFY: return Destination( SeedWordsBackupTestView, - view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data), + view_args=dict(seed=self.seed, bip85_data=self.bip85_data), ) elif button_data[selected_menu_num] == self.SKIP: - if self.seed_num is not None: - return Destination(SeedOptionsView, view_args=dict(seed_num=self.seed_num)) - else: + if self.seed is None: return Destination(SeedFinalizeView) + else: + return Destination(SeedOptionsView, view_args=dict(seed=self.seed)) class SeedWordsBackupTestView(View): - def __init__(self, seed_num: int, bip85_data: dict = None, confirmed_list: list[bool] = None, cur_index: int = None, rand_seed: int = None): + def __init__(self, seed: Seed, bip85_data: dict = None, confirmed_list: list[bool] = None, cur_index: int = None, rand_seed: int = None): """ Note: `rand_seed` is ONLY USED BY THE SCREENSHOT GENERATOR!!! (to ensure consistent screenshot results). """ super().__init__() - self.seed_num = seed_num - if self.seed_num is None: + if seed is None: + self.is_pending_seed = True self.seed = self.controller.storage.get_pending_seed() else: - self.seed = self.controller.get_seed(self.seed_num) + self.is_pending_seed = False + self.seed = seed self.bip85_data = bip85_data if self.bip85_data is not None: @@ -1296,19 +1299,22 @@ def run(self): is_button_text_centered=True, ) + if self.is_pending_seed: + self.seed = None # Set to None for next View to know it's a pending seed + if button_data[selected_menu_num] == real_word: self.confirmed_list.append(self.cur_index) if len(self.confirmed_list) == len(self.mnemonic_list): # Successfully confirmed the full mnemonic! return Destination( SeedWordsBackupTestSuccessView, - view_args=dict(seed_num=self.seed_num), + view_args=dict(seed=self.seed), ) else: # Continue testing the remaining words return Destination( SeedWordsBackupTestView, - view_args=dict(seed_num=self.seed_num, confirmed_list=self.confirmed_list, bip85_data=self.bip85_data), + view_args=dict(seed=self.seed, confirmed_list=self.confirmed_list, bip85_data=self.bip85_data), ) else: @@ -1316,7 +1322,7 @@ def run(self): return Destination( SeedWordsBackupTestMistakeView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, bip85_data=self.bip85_data, cur_index=self.cur_index, wrong_word=button_data[selected_menu_num].button_label, @@ -1330,9 +1336,9 @@ class SeedWordsBackupTestMistakeView(View): REVIEW = ButtonOption("Review seed words") RETRY = ButtonOption("Try again") - def __init__(self, seed_num: int, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: list[bool] = None): + def __init__(self, seed: Seed, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: list[bool] = None): super().__init__() - self.seed_num = seed_num + self.seed = seed self.bip85_data = bip85_data self.cur_index = cur_index self.wrong_word = wrong_word @@ -1361,14 +1367,14 @@ def run(self): if button_data[selected_menu_num] == self.REVIEW: return Destination( SeedWordsView, - view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data), + view_args=dict(seed=self.seed, bip85_data=self.bip85_data), ) elif button_data[selected_menu_num] == self.RETRY: return Destination( SeedWordsBackupTestView, view_args=dict( - seed_num=self.seed_num, + seed=self.seed, confirmed_list=self.confirmed_list, cur_index=self.cur_index, bip85_data=self.bip85_data, @@ -1378,9 +1384,9 @@ def run(self): class SeedWordsBackupTestSuccessView(View): - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num + self.seed = seed def run(self): from seedsigner.gui.screens.screen import LargeIconStatusScreen @@ -1393,10 +1399,10 @@ def run(self): button_data=[ButtonOption("OK")] ) - if self.seed_num is not None: - return Destination(SeedOptionsView, view_args=dict(seed_num=self.seed_num), clear_history=True) - else: + if self.seed is None: return Destination(SeedFinalizeView) + else: + return Destination(SeedOptionsView, view_args=dict(seed=self.seed), clear_history=True) @@ -1412,27 +1418,26 @@ class SeedTranscribeSeedQRFormatView(View): STANDARD_24 = ButtonOption("Standard: 29x29", return_data=29) COMPACT_24 = ButtonOption("Compact: 25x25", return_data=25) - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num + self.seed = seed def run(self): - seed = self.controller.get_seed(self.seed_num) if self.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) != SettingsConstants.OPTION__ENABLED: # Only configured for standard SeedQR return Destination( SeedTranscribeSeedQRWarningView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "seedqr_format": QRType.SEED__SEEDQR, "num_modules": self.STANDARD_12.return_data, }, skip_current_view=True, ) - if len(seed.mnemonic_list) == 12: + if len(self.seed.mnemonic_list) == 12: button_data = [self.STANDARD_12, self.COMPACT_12] else: button_data = [self.STANDARD_24, self.COMPACT_24] @@ -1456,7 +1461,7 @@ def run(self): return Destination( SeedTranscribeSeedQRWarningView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "seedqr_format": seedqr_format, "num_modules": num_modules, } @@ -1465,9 +1470,9 @@ def run(self): class SeedTranscribeSeedQRWarningView(View): - def __init__(self, seed_num: int, seedqr_format: str = QRType.SEED__SEEDQR, num_modules: int = 29): + def __init__(self, seed: Seed, seedqr_format: str = QRType.SEED__SEEDQR, num_modules: int = 29): super().__init__() - self.seed_num = seed_num + self.seed = seed self.seedqr_format = seedqr_format self.num_modules = num_modules @@ -1476,7 +1481,7 @@ def run(self): destination = Destination( SeedTranscribeSeedQRWholeQRView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "seedqr_format": self.seedqr_format, "num_modules": self.num_modules, }, @@ -1503,12 +1508,11 @@ def run(self): class SeedTranscribeSeedQRWholeQRView(View): - def __init__(self, seed_num: int, seedqr_format: str, num_modules: int): + def __init__(self, seed: Seed, seedqr_format: str, num_modules: int): super().__init__() - self.seed_num = seed_num + self.seed = seed self.seedqr_format = seedqr_format self.num_modules = num_modules - self.seed = self.controller.get_seed(seed_num) def run(self): @@ -1534,7 +1538,7 @@ def run(self): return Destination( SeedTranscribeSeedQRZoomedInView, view_args={ - "seed_num": self.seed_num, + "seed": self.seed, "seedqr_format": self.seedqr_format } ) @@ -1546,11 +1550,10 @@ class SeedTranscribeSeedQRZoomedInView(View): intial_zone_x, initial_zone_y: Used by the screenshot generator to shift the view to a more interesting part of the QR code template. """ - def __init__(self, seed_num: int, seedqr_format: str, initial_zone_x: int = 0, initial_zone_y: int = 0): + def __init__(self, seed: Seed, seedqr_format: str, initial_zone_x: int = 0, initial_zone_y: int = 0): super().__init__() - self.seed_num = seed_num + self.seed = seed self.seedqr_format = seedqr_format - self.seed = self.controller.get_seed(seed_num) self.initial_zone_x = initial_zone_x self.initial_zone_y = initial_zone_y self.is_screensaver_allowed = False @@ -1585,7 +1588,7 @@ def run(self): initial_zone_y=self.initial_zone_y, ) - return Destination(SeedTranscribeSeedQRConfirmQRPromptView, view_args={"seed_num": self.seed_num}) + return Destination(SeedTranscribeSeedQRConfirmQRPromptView, view_args={"seed": self.seed}) @@ -1593,10 +1596,9 @@ class SeedTranscribeSeedQRConfirmQRPromptView(View): SCAN = ButtonOption("Confirm SeedQR", SeedSignerIconConstants.QRCODE) DONE = ButtonOption("Done") - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num - self.seed = self.controller.get_seed(seed_num) + self.seed = seed def run(self): @@ -1612,19 +1614,18 @@ def run(self): return Destination(BackStackView) elif button_data[selected_menu_option] == self.SCAN: - return Destination(SeedTranscribeSeedQRConfirmScanView, view_args={"seed_num": self.seed_num}) + return Destination(SeedTranscribeSeedQRConfirmScanView, view_args={"seed": self.seed}) elif button_data[selected_menu_option] == self.DONE: - return Destination(SeedOptionsView, view_args={"seed_num": self.seed_num}, clear_history=True) + return Destination(SeedOptionsView, view_args={"seed": self.seed}, clear_history=True) class SeedTranscribeSeedQRConfirmScanView(View): - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): from seedsigner.models.decode_qr import DecodeQR super().__init__() - self.seed_num = seed_num - self.seed = self.controller.get_seed(seed_num) + self.seed = seed wordlist_language_code = self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE) self.decoder = DecodeQR(wordlist_language_code=wordlist_language_code) @@ -1650,7 +1651,7 @@ def run(self): if seed_mnemonic != self.seed.mnemonic_list: return Destination(SeedTranscribeSeedQRConfirmWrongSeedView, skip_current_view=True) else: - return Destination(SeedTranscribeSeedQRConfirmSuccessView, view_args={"seed_num": self.seed_num}) + return Destination(SeedTranscribeSeedQRConfirmSuccessView, view_args={"seed": self.seed}) # Will trigger if a different kind of QR code is scanned (non SeedQR) return Destination(SeedTranscribeSeedQRConfirmInvalidQRView, skip_current_view=True) @@ -1701,9 +1702,9 @@ class SeedTranscribeSeedQRConfirmSuccessView(View): """ The SeedQR we just scanned matched the one we just transcribed. """ - def __init__(self, seed_num: int): + def __init__(self, seed: Seed): super().__init__() - self.seed_num = seed_num + self.seed = seed def run(self): @@ -1717,7 +1718,7 @@ def run(self): button_data=[ButtonOption("OK")], ) - return Destination(SeedOptionsView, view_args={"seed_num": self.seed_num}) + return Destination(SeedOptionsView, view_args={"seed": self.seed}) @@ -1831,27 +1832,23 @@ class SeedAddressVerificationView(View): The `ThreadsafeCounter` is sent to the display Screen which is monitored in its own `ProgressThread` to show the current iteration onscreen. - Performs single sig verification on `seed_num` if specified, otherwise assumes + Performs single sig verification on `seed` if specified, otherwise assumes multisig. """ # TRANSLATOR_NOTE: Option when scanning for a matching address; skips ten addresses ahead SKIP_10 = ButtonOption("Skip 10") CANCEL = ButtonOption("Cancel") - def __init__(self, seed_num: int = None): + def __init__(self, seed: Seed = None): super().__init__() - self.seed_num = seed_num + self.seed = seed self.is_multisig = self.controller.unverified_address["sig_type"] == SettingsConstants.MULTISIG self.seed_derivation_override = "" if not self.is_multisig: - if seed_num is None: + if self.seed is None: # Shouldn't be able to get here raise Exception("Can't validate a single sig addr without specifying a seed") - self.seed_num = seed_num - self.seed = self.controller.get_seed(seed_num) self.seed_derivation_override = self.seed.derivation_override(sig_type=SettingsConstants.SINGLE_SIG) - else: - self.seed = None self.address = self.controller.unverified_address["address"] self.derivation_path = self.seed_derivation_override if self.seed_derivation_override else self.controller.unverified_address["derivation_path"] self.script_type = self.controller.unverified_address["script_type"] @@ -1873,7 +1870,7 @@ def __init__(self, seed_num: int = None): # Create the brute-force calculation thread that will run in the background self.addr_verification_thread = self.BruteForceAddressVerificationThread( address=self.address, - seed=self.seed, + seed=None if self.is_multisig else self.seed, descriptor=self.controller.multisig_wallet_descriptor, script_type=self.script_type, embit_network=embit_network, @@ -1941,7 +1938,7 @@ def run(self): # Successfully verified the addr; update the data self.controller.unverified_address["verified_index"] = self.verified_index.cur_count self.controller.unverified_address["verified_index_is_change"] = self.verified_index_is_change.cur_count == 1 - return Destination(SeedAddressVerificationSuccessView, view_args=dict(seed_num=self.seed_num)) + return Destination(SeedAddressVerificationSuccessView) finally: # Halt the thread if the user gave up (will already be stopped if it verified the @@ -2010,13 +2007,6 @@ def run(self): class SeedAddressVerificationSuccessView(View): - def __init__(self, seed_num: int): - super().__init__() - self.seed_num = seed_num - if self.seed_num is not None: - self.seed = self.controller.get_seed(seed_num) - - def run(self): self.run_screen( seed_screens.SeedAddressVerificationSuccessScreen, @@ -2119,7 +2109,7 @@ def run(self): ****************************************************************************""" class SeedSignMessageStartView(View): """ - Routes users straight through to the "Sign" screen if a signing `seed_num` has + Routes users straight through to the "Sign" screen if a signing `seed` has already been selected. Otherwise routes to `SeedSelectSeedView` to select or load a seed first. """ @@ -2151,17 +2141,14 @@ def __init__(self, derivation_path: str, message: str): return data = self.controller.sign_message_data - if not data: + if data is None: data = {} self.controller.sign_message_data = data data["derivation_path"] = derivation_path data["message"] = message data["addr_format"] = addr_format - # May be None - self.seed_num = data.get("seed_num") - - if self.seed_num is not None: + if data.get("seed") is not None: # We already know which seed we're signing with self.set_redirect(Destination(SeedSignMessageConfirmMessageView, skip_current_view=True)) else: @@ -2205,18 +2192,13 @@ def __init__(self): from seedsigner.helpers import embit_utils super().__init__() data = self.controller.sign_message_data - seed_num = data.get("seed_num") + seed = data.get("seed") self.derivation_path = data.get("derivation_path") + addr_format = data.get("addr_format") - if seed_num is None or not self.derivation_path: + if seed is None or not self.derivation_path: raise Exception("Routing error: sign_message_data hasn't been set") - seed = self.controller.storage.seeds[seed_num] - addr_format = data.get("addr_format") - - # calculate the actual receive address - seed = self.controller.storage.seeds[seed_num] - addr_format = embit_utils.parse_derivation_path(self.derivation_path) if not addr_format["clean_match"] or addr_format["script_type"] == SettingsConstants.CUSTOM_DERIVATION: raise Exception(_("Signing messages for custom derivation paths not supported")) @@ -2264,12 +2246,11 @@ def __init__(self): super().__init__() data = self.controller.sign_message_data - self.seed_num = data["seed_num"] - seed = self.controller.get_seed(self.seed_num) + self.seed = data["seed"] derivation_path = data["derivation_path"] message: str = data["message"] - self.signed_message = embit_utils.sign_message(seed_bytes=seed.seed_bytes, derivation=derivation_path, msg=message.encode()) + self.signed_message = embit_utils.sign_message(seed_bytes=self.seed.seed_bytes, derivation=derivation_path, msg=message.encode()) def run(self): diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index 798cf5ba0..a25e3ae7d 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -195,7 +195,7 @@ def run(self): self.loading_screen.stop() # Cannot return BACK to this View - return Destination(SeedWordsWarningView, view_args={"seed_num": None}, clear_history=True) + return Destination(SeedWordsWarningView, view_args={"seed": None}, clear_history=True) @@ -258,7 +258,7 @@ def run(self): self.controller.storage.set_pending_seed(seed) # Cannot return BACK to this View - return Destination(SeedWordsWarningView, view_args={"seed_num": None}, clear_history=True) + return Destination(SeedWordsWarningView, view_args={"seed": None}, clear_history=True) @@ -510,11 +510,11 @@ def run(self): self.controller.resume_main_flow = Controller.FLOW__ADDRESS_EXPLORER if len(seeds) > 0 and selected_menu_num < len(seeds): - # User selected one of the n seeds + selected_seed = seeds[selected_menu_num] return Destination( SeedExportXpubScriptTypeView, view_args=dict( - seed_num=selected_menu_num, + seed=selected_seed, sig_type=SettingsConstants.SINGLE_SIG, ) ) @@ -546,32 +546,30 @@ class ToolsAddressExplorerAddressTypeView(View): CHANGE = ButtonOption("Change addresses") - def __init__(self, seed_num: int = None, script_type: str = None, custom_derivation: str = None): + def __init__(self, seed: Seed = None, script_type: str = None, custom_derivation: str = None): """ - If the explorer source is a seed, `seed_num` and `script_type` must be + If the explorer source is a seed, `seed` and `script_type` must be specified. `custom_derivation` can be specified as needed. - If the source is a multisig or single sig wallet descriptor, `seed_num`, + If the source is a multisig or single sig wallet descriptor, `seed`, `script_type`, and `custom_derivation` should be `None`. """ super().__init__() - self.seed_num = seed_num + self.seed = seed self.script_type = script_type self.custom_derivation = custom_derivation - network = self.settings.get_value(SettingsConstants.SETTING__NETWORK) + self.network = self.settings.get_value(SettingsConstants.SETTING__NETWORK) # Store everything in the Controller's `address_explorer_data` so we don't have # to keep passing vals around from View to View and recalculating. data = dict( - seed_num=seed_num, - network=self.settings.get_value(SettingsConstants.SETTING__NETWORK), - embit_network=SettingsConstants.map_network_to_embit(network), + seed=self.seed, + network=self.network, + embit_network=SettingsConstants.map_network_to_embit(self.network), script_type=script_type, ) - if self.seed_num is not None: - self.seed = self.controller.storage.seeds[seed_num] - data["seed_num"] = self.seed + if self.seed is not None: seed_derivation_override = self.seed.derivation_override(sig_type=SettingsConstants.SINGLE_SIG) if self.script_type == SettingsConstants.CUSTOM_DERIVATION: @@ -581,13 +579,13 @@ def __init__(self, seed_num: int = None, script_type: str = None, custom_derivat else: from seedsigner.helpers import embit_utils derivation_path = embit_utils.get_standard_derivation_path( - network=self.settings.get_value(SettingsConstants.SETTING__NETWORK), + network=self.network, wallet_type=SettingsConstants.SINGLE_SIG, script_type=self.script_type, ) data["derivation_path"] = derivation_path - data["xpub"] = self.seed.get_xpub(derivation_path, network=network) + data["xpub"] = self.seed.get_xpub(derivation_path, network=self.network) else: data["wallet_descriptor"] = self.controller.multisig_wallet_descriptor @@ -615,7 +613,7 @@ def run(self): selected_menu_num = self.run_screen( ToolsAddressExplorerAddressTypeScreen, button_data=button_data, - fingerprint=self.seed.get_fingerprint() if self.seed_num is not None else None, + fingerprint=None if self.seed is None else self.seed.get_fingerprint(self.network), wallet_descriptor_display_name=wallet_descriptor_display_name, script_type=script_type, custom_derivation_path=self.custom_derivation, diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py index de2db1a8d..ecfd13a17 100644 --- a/tests/screenshot_generator/generator.py +++ b/tests/screenshot_generator/generator.py @@ -190,7 +190,7 @@ def setup_screenshots(locale: str) -> dict[str, list[ScreenshotConfig]]: # Message signing data derivation_path = "m/84h/0h/0h/0/0" controller.sign_message_data = { - "seed_num": 0, + "seed": seed_12, "derivation_path": derivation_path, "message": "I attest that I control this bitcoin address blah blah blah", "addr_format": embit_utils.parse_derivation_path(derivation_path) @@ -374,51 +374,51 @@ def mock_version_to_most_recent_release(): ScreenshotConfig(seed_views.SeedAddPassphraseExitDialogView), ScreenshotConfig(seed_views.SeedReviewPassphraseView), - ScreenshotConfig(seed_views.SeedOptionsView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedBackupView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedExportXpubSigTypeView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedExportXpubScriptTypeView, dict(seed_num=0, sig_type="msig")), - ScreenshotConfig(seed_views.SeedExportXpubCustomDerivationView, dict(seed_num=0, sig_type="ss", script_type="")), - ScreenshotConfig(seed_views.SeedExportXpubQRFormatView, dict(seed_num=0, sig_type="ss", script_type="nat")), - ScreenshotConfig(seed_views.SeedExportXpubWarningView, dict(seed_num=0, sig_type="msig", script_type="nes", xpub_qr_format="urca", custom_derivation="")), - ScreenshotConfig(seed_views.SeedExportXpubDetailsView, dict(seed_num=0, sig_type="ss", script_type="nat", xpub_qr_format="urca", custom_derivation="")), - ScreenshotConfig(SeedExportXpubQR_ScreenBrightnessView, dict(seed_num=0, xpub_qr_format="urca", derivation_path="m/84'/0'/0'")), - - ScreenshotConfig(seed_views.SeedWordsWarningView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0, page_index=2), screenshot_name="SeedWordsView_2"), - ScreenshotConfig(seed_views.SeedBIP85SelectNumWordsView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedBIP85SelectChildIndexView, dict(seed_num=0, num_words=24)), - ScreenshotConfig(seed_views.SeedBIP85InvalidChildIndexView, dict(seed_num=0, num_words=12)), - ScreenshotConfig(seed_views.SeedWordsBackupTestPromptView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedWordsBackupTestView, dict(seed_num=0, rand_seed=6102)), - ScreenshotConfig(seed_views.SeedWordsBackupTestMistakeView, dict(seed_num=0, cur_index=7, wrong_word="satoshi")), - ScreenshotConfig(seed_views.SeedWordsBackupTestSuccessView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRFormatView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRWarningView, dict(seed_num=0)), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=21), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Compact"), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Standard"), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Compact"), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__SEEDQR, num_modules=29), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Standard"), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, initial_zone_x=1, initial_zone_y=1), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Compact"), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, initial_zone_x=2, initial_zone_y=2), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Standard"), - - ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, dict(seed_num=0)), + ScreenshotConfig(seed_views.SeedOptionsView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedBackupView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedExportXpubSigTypeView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedExportXpubScriptTypeView, dict(seed=seed_12, sig_type="msig")), + ScreenshotConfig(seed_views.SeedExportXpubCustomDerivationView, dict(seed=seed_12, sig_type="ss", script_type="")), + ScreenshotConfig(seed_views.SeedExportXpubQRFormatView, dict(seed=seed_12, sig_type="ss", script_type="nat")), + ScreenshotConfig(seed_views.SeedExportXpubWarningView, dict(seed=seed_12, sig_type="msig", script_type="nes", xpub_qr_format="urca", custom_derivation="")), + ScreenshotConfig(seed_views.SeedExportXpubDetailsView, dict(seed=seed_12, sig_type="ss", script_type="nat", xpub_qr_format="urca", custom_derivation="")), + ScreenshotConfig(SeedExportXpubQR_ScreenBrightnessView, dict(seed=seed_12, xpub_qr_format="urca", derivation_path="m/84'/0'/0'")), + + ScreenshotConfig(seed_views.SeedWordsWarningView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedWordsView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedWordsView, dict(seed=seed_12, page_index=2), screenshot_name="SeedWordsView_2"), + ScreenshotConfig(seed_views.SeedBIP85SelectNumWordsView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedBIP85SelectChildIndexView, dict(seed=seed_12, num_words=24)), + ScreenshotConfig(seed_views.SeedBIP85InvalidChildIndexView, dict(seed=seed_12, num_words=12)), + ScreenshotConfig(seed_views.SeedWordsBackupTestPromptView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedWordsBackupTestView, dict(seed=seed_12, rand_seed=6102)), + ScreenshotConfig(seed_views.SeedWordsBackupTestMistakeView, dict(seed=seed_12, cur_index=7, wrong_word="satoshi")), + ScreenshotConfig(seed_views.SeedWordsBackupTestSuccessView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRFormatView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRWarningView, dict(seed=seed_12)), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed=seed_12, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=21), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Compact"), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed=seed_12, seedqr_format=QRType.SEED__SEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Standard"), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed=seed_24, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Compact"), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed=seed_24, seedqr_format=QRType.SEED__SEEDQR, num_modules=29), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Standard"), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed=seed_12, seedqr_format=QRType.SEED__COMPACTSEEDQR, initial_zone_x=1, initial_zone_y=1), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Compact"), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed=seed_12, seedqr_format=QRType.SEED__SEEDQR, initial_zone_x=2, initial_zone_y=2), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Standard"), + + ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, dict(seed=seed_12)), ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmWrongSeedView), ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmInvalidQRView), - ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmSuccessView, dict(seed_num=0)), + ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmSuccessView, dict(seed=seed_12)), # Screenshot can't render live preview screens - # ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmScanView, dict(seed_num=0)), + # ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmScanView, dict(seed=seed_12)), ScreenshotConfig(seed_views.SeedSelectSeedView, dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), screenshot_name="SeedSelectSeedView_address_verification"), ScreenshotConfig(seed_views.AddressVerificationSigTypeView), - ScreenshotConfig(seed_views.SeedAddressVerificationView, dict(seed_num=0), mock_context_manager=mock_address_verification_data_loaded), - ScreenshotConfig(seed_views.SeedAddressVerificationSuccessView, dict(seed_num=0), mock_context_manager=mock_address_verification_data_loaded), + ScreenshotConfig(seed_views.SeedAddressVerificationView, dict(seed=seed_12), mock_context_manager=mock_address_verification_data_loaded), + ScreenshotConfig(seed_views.SeedAddressVerificationSuccessView, mock_context_manager=mock_address_verification_data_loaded), ScreenshotConfig(seed_views.LoadMultisigWalletDescriptorView), ScreenshotConfig(seed_views.MultisigWalletDescriptorView, mock_context_manager=mock_multisig_wallet_descriptor_loaded), - ScreenshotConfig(seed_views.SeedDiscardView, dict(seed_num=0)), + ScreenshotConfig(seed_views.SeedDiscardView, dict(seed=seed_12)), ScreenshotConfig(seed_views.SeedSelectSeedView, dict(flow=Controller.FLOW__SIGN_MESSAGE), screenshot_name="SeedSelectSeedView_sign_message"), ScreenshotConfig(seed_views.SeedSignMessageConfirmMessageView), diff --git a/tests/test_flows.py b/tests/test_flows.py index e520de482..47eb02773 100644 --- a/tests/test_flows.py +++ b/tests/test_flows.py @@ -142,7 +142,7 @@ def test_initial_destination(self): self.controller.storage.finalize_pending_seed() self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(SeedOptionsView, button_data_selection=SeedOptionsView.BACKUP), FlowStep(SeedBackupView), diff --git a/tests/test_flows_psbt.py b/tests/test_flows_psbt.py index 4141e5754..1e3067de9 100644 --- a/tests/test_flows_psbt.py +++ b/tests/test_flows_psbt.py @@ -57,6 +57,18 @@ def load_seed_into_decoder(view: scan_views.ScanView): FlowStep(MainMenuView) ]) + # Run the same PSBT flow again, this time selecting the seed that the + # previous flow left loaded in memory. + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), + FlowStep(scan_views.ScanView, before_run=load_psbt_into_decoder), + FlowStep(psbt_views.PSBTSelectSeedView, screen_return_value=0), + FlowStep(psbt_views.PSBTOverviewView), + ]) + + # Selecting the existing seed should have set it as the signing seed + assert self.controller.psbt_seed is self.controller.storage.seeds[0] + def test_scan_psbt_first_then_load_electrum_seed(self): """ diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index 578f0094f..5e979fa4d 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -188,7 +188,7 @@ def flowtest_standard_xpub(sig_tuple, script_tuple, xpub_qr_tuple): else: sig_selection = seed_views.SeedExportXpubSigTypeView.MULTISIG self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_selection), @@ -202,8 +202,8 @@ def flowtest_standard_xpub(sig_tuple, script_tuple, xpub_qr_tuple): ) # Load a finalized Seed into the Controller - mnemonic = "blush twice taste dawn feed second opinion lazy thumb play neglect impact".split() - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic="blush twice taste dawn feed second opinion lazy thumb play neglect impact".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() # these are lists of (constant_value, display_name) tuples @@ -236,8 +236,8 @@ def test_export_xpub_disabled_not_available_flow(self): If sig_type/script_type/xpub_qr_format disabled, then these options are not available """ # Load a finalized Seed into the Controller - mnemonic = "blush twice taste dawn feed second opinion lazy thumb play neglect impact".split() - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic="blush twice taste dawn feed second opinion lazy thumb play neglect impact".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() # these are lists of (constant_value, display_name) tuples @@ -257,7 +257,7 @@ def test_export_xpub_disabled_not_available_flow(self): # If multisig isn't an option, then the sig type selection is skipped altogether self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True), @@ -268,7 +268,7 @@ def test_export_xpub_disabled_not_available_flow(self): # test that taproot is not an option via exception raised when choice is taproot with pytest.raises(FlowTestInvalidButtonDataSelectionException) as e: self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True), @@ -279,7 +279,7 @@ def test_export_xpub_disabled_not_available_flow(self): # test that nunchuk is not an option via exception raised when choice is nunchuk with pytest.raises(FlowTestInvalidButtonDataSelectionException) as e: self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True), @@ -294,8 +294,8 @@ def test_export_xpub_custom_derivation_flow(self): Export XPUB flow for custom derivation finishes at MainMenuView """ # Load a finalized Seed into the Controller - mnemonic = "blush twice taste dawn feed second opinion lazy thumb play neglect impact".split() - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic="blush twice taste dawn feed second opinion lazy thumb play neglect impact".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() # enable custom derivation script_type setting (plus at least one more for a choice) @@ -319,7 +319,7 @@ def test_export_xpub_custom_derivation_flow(self): xpub_qr_format = ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__XPUB_QR_FORMAT)[2], return_data=specter_legacy) self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_type), @@ -339,8 +339,8 @@ def test_export_xpub_skip_non_option_flow(self): Export XPUB flows w/o user choices when no other options for sig_types, script_types, and/or xpub_qr_formats """ # Load a finalized Seed into the Controller - mnemonic = "blush twice taste dawn feed second opinion lazy thumb play neglect impact".split() - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic="blush twice taste dawn feed second opinion lazy thumb play neglect impact".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() # exclusively set only one choice for each of sig_types, script_types and xpub_qr_formats @@ -351,7 +351,7 @@ def test_export_xpub_skip_non_option_flow(self): }) self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True), @@ -371,7 +371,8 @@ def test_export_xpub_electrum_seed_flow(self): """ # Load a finalized Seed into the Controller self.controller.storage.init_pending_mnemonic(num_words=12, is_electrum=True) - self.controller.storage.set_pending_seed(ElectrumSeed("regular reject rare profit once math fringe chase until ketchup century escape".split())) + seed = ElectrumSeed(mnemonic="regular reject rare profit once math fringe chase until ketchup century escape".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() # Make sure all options are enabled @@ -380,7 +381,7 @@ def test_export_xpub_electrum_seed_flow(self): self.settings.set_value(SettingsConstants.SETTING__XPUB_QR_FORMAT, [x for x,y in SettingsConstants.ALL_XPUB_QR_FORMATS]) self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB), FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=seed_views.SeedExportXpubSigTypeView.SINGLE_SIG), @@ -402,12 +403,12 @@ def test_discard_seed_flow(self): remove the in-memory seed from the Controller. """ # Load a finalized Seed into the Controller - mnemonic = "blush twice taste dawn feed second opinion lazy thumb play neglect impact".split() - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic="blush twice taste dawn feed second opinion lazy thumb play neglect impact".split()) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() self.run_sequence( - initial_destination_view_args=dict(seed_num=0), + initial_destination_view_args=dict(seed=seed), sequence=[ FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.DISCARD), FlowStep(seed_views.SeedDiscardView, button_data_selection=seed_views.SeedDiscardView.DISCARD), @@ -481,11 +482,12 @@ def test_transcribe_seedqr_screensaver_startable_status(self): """ # Load a finalized Seed into the Controller mnemonic = ["abandon"] * 11 + ["about"] - self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + seed = Seed(mnemonic=mnemonic) + self.controller.storage.set_pending_seed(seed) self.controller.storage.finalize_pending_seed() self.run_sequence( - initial_destination_view_args={'num_modules': 21, 'seed_num': 0, 'seedqr_format': 'seed__seedqr'}, + initial_destination_view_args={'num_modules': 21, 'seed': seed, 'seedqr_format': 'seed__seedqr'}, sequence=[ FlowStep(seed_views.SeedTranscribeSeedQRWholeQRView), FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are a bit weird; not sure why `is_redirect` is necessary here @@ -646,7 +648,7 @@ def test_sign_message_flow(self): ]) # Scenario 2: Scan the seed first, then select Sign Message - self.controller.discard_seed(0) + self.controller.discard_seed(self.controller.storage.seeds[0]) self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored @@ -694,7 +696,7 @@ def test_sign_message_flow(self): ]) # Scenario 4: Load a long message without whitespace - self.controller.discard_seed(0) + self.controller.discard_seed(self.controller.storage.seeds[0]) self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored diff --git a/tests/test_flows_tools.py b/tests/test_flows_tools.py index a7bfa2c47..260678bc9 100644 --- a/tests/test_flows_tools.py +++ b/tests/test_flows_tools.py @@ -177,7 +177,7 @@ def load_seed_into_decoder(view: scan_views.ScanView): # Scenario 4: No seed onboard, one script type enabled, started from Tools, BACK # can only go to MainMenu because of mid-flow seed load. - controller.discard_seed(0) + controller.discard_seed(seed) self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.TOOLS), FlowStep(tools_views.ToolsMenuView, button_data_selection=tools_views.ToolsMenuView.ADDRESS_EXPLORER),