Repository: seedsigner/seedsigner
Issue: xpub spinning goes on forever

If you generate an xpub with an invalid derivation, it will error, but the spinner will not disappear until you restart the SeedSigner.


Changed files:
src/seedsigner/views/seed_views.py, src/seedsigner/views/psbt_views.py

diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py
index 1b8da55..b515038 100644
--- a/src/seedsigner/views/psbt_views.py
+++ b/src/seedsigner/views/psbt_views.py
@@ -32,7 +32,8 @@ class PSBTSelectSeedView(View):
         seeds = self.controller.storage.seeds
 
         SCAN_SEED = ("Scan a seed", FontAwesomeIconConstants.QRCODE)
-        ENTER_WORDS = "Enter 12/24 words"
+        TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
+        TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
         button_data = []
         for seed in seeds:
             button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
@@ -45,7 +46,8 @@ class PSBTSelectSeedView(View):
                 pass
             button_data.append((button_str, SeedSignerCustomIconConstants.FINGERPRINT, "blue"))
         button_data.append(SCAN_SEED)
-        button_data.append(ENTER_WORDS)
+        button_data.append(TYPE_12WORD)
+        button_data.append(TYPE_24WORD)
 
         selected_menu_num = ButtonListScreen(
             title="Select Signer",
@@ -68,8 +70,12 @@ class PSBTSelectSeedView(View):
             from seedsigner.views.scan_views import ScanView
             return Destination(ScanView)
 
-        elif button_data[selected_menu_num] == ENTER_WORDS:
+        elif button_data[selected_menu_num] in [TYPE_12WORD, TYPE_24WORD]:
             from seedsigner.views.seed_views import SeedMnemonicEntryView
+            if button_data[selected_menu_num] == TYPE_12WORD:
+                self.controller.storage.init_pending_mnemonic(num_words=12)
+            else:
+                self.controller.storage.init_pending_mnemonic(num_words=24)
             return Destination(SeedMnemonicEntryView)
 
 
@@ -78,18 +84,22 @@ class PSBTOverviewView(View):
     def __init__(self):
         super().__init__()
 
-        # The PSBTParser takes a while to read the PSBT. Run the loading screen while we
-        # wait.
-        self.loading_screen = LoadingScreenThread(text="Parsing PSBT...")
-        self.loading_screen.start()
+        self.loading_screen = None
 
         if not self.controller.psbt_parser or self.controller.psbt_parser.seed != self.controller.psbt_seed:
-            # Must run the PSBTParser or re-parse
-            self.controller.psbt_parser = PSBTParser(
-                self.controller.psbt,
-                seed=self.controller.psbt_seed,
-                network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)
-            )
+            # The PSBTParser takes a while to read the PSBT. Run the loading screen while
+            # we wait.
+            self.loading_screen = LoadingScreenThread(text="Parsing PSBT...")
+            self.loading_screen.start()
+            try:
+                self.controller.psbt_parser = PSBTParser(
+                    self.controller.psbt,
+                    seed=self.controller.psbt_seed,
+                    network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)
+                )
+            except Exception as e:
+                self.loading_screen.stop()
+                raise e
 
 
     def run(self):
@@ -126,7 +136,8 @@ class PSBTOverviewView(View):
         )
 
         # Everything is set. Stop the loading screen
-        self.loading_screen.stop()
+        if self.loading_screen:
+            self.loading_screen.stop()
 
         # Run the overview screen
         selected_menu_num = screen.display()
@@ -145,6 +156,8 @@ class PSBTOverviewView(View):
         else:
             return Destination(PSBTMathView)
 
+
+
 class PSBTUnsupportedScriptTypeWarningView(View):
     def run(self):
         selected_menu_num = WarningScreen(
@@ -462,11 +475,12 @@ class PSBTFinalizeView(View):
             loading_screen = LoadingScreenThread(text="Signing PSBT...")
             loading_screen.start()
 
-            sig_cnt = PSBTParser.sig_count(psbt)
-            psbt.sign_with(psbt_parser.root)
-            trimmed_psbt = PSBTParser.trim(psbt)
-
-            loading_screen.stop()
+            try:
+                sig_cnt = PSBTParser.sig_count(psbt)
+                psbt.sign_with(psbt_parser.root)
+                trimmed_psbt = PSBTParser.trim(psbt)
+            finally:
+                loading_screen.stop()
 
             if sig_cnt == PSBTParser.sig_count(trimmed_psbt):
                 # Signing failed / didn't do anything
diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py
index e8eb28c..5370ecd 100644
--- a/src/seedsigner/views/seed_views.py
+++ b/src/seedsigner/views/seed_views.py
@@ -73,13 +73,13 @@ class SeedsMenuView(View):
 class LoadSeedView(View):
     def run(self):
         SEED_QR = (" Scan a SeedQR", FontAwesomeIconConstants.QRCODE)
-        TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
         TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
+        TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
         CREATE = (" Create a seed", FontAwesomeIconConstants.PLUS)
         button_data=[
             SEED_QR,
-            TYPE_24WORD,
             TYPE_12WORD,
+            TYPE_24WORD,
             CREATE,
         ]
 
@@ -96,14 +96,14 @@ class LoadSeedView(View):
             from .scan_views import ScanView
             return Destination(ScanView)
         
-        elif button_data[selected_menu_num] == TYPE_24WORD:
-            self.controller.storage.init_pending_mnemonic(num_words=24)
-            return Destination(SeedMnemonicEntryView)
-
         elif button_data[selected_menu_num] == TYPE_12WORD:
             self.controller.storage.init_pending_mnemonic(num_words=12)
             return Destination(SeedMnemonicEntryView)
 
+        elif button_data[selected_menu_num] == TYPE_24WORD:
+            self.controller.storage.init_pending_mnemonic(num_words=24)
+            return Destination(SeedMnemonicEntryView)
+
         elif button_data[selected_menu_num] == CREATE:
             from .tools_views import ToolsMenuView
             return Destination(ToolsMenuView)
@@ -648,46 +648,50 @@ class SeedExportXpubDetailsView(View):
         self.loading_screen = LoadingScreenThread(text="Generating xpub...")
         self.loading_screen.start()
 
-        if self.script_type == SettingsConstants.CUSTOM_DERIVATION:
-            derivation_path = self.custom_derivation
-        else:
-            derivation_path = PSBTParser.calc_derivation(
-                network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
-                wallet_type=self.sig_type,
-                script_type=self.script_type
-            )
+        try:
+            if self.script_type == SettingsConstants.CUSTOM_DERIVATION:
+                derivation_path = self.custom_derivation
+            else:
+                derivation_path = PSBTParser.calc_derivation(
+                    network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
+                    wallet_type=self.sig_type,
+                    script_type=self.script_type
+                )
 
-        if self.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__ENABLED:
-            embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))]
-            version = embit.bip32.detect_version(
-                derivation_path,
-                default="xpub",
-                network=embit_network
-            )
+            if self.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__ENABLED:
+                embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))]
+                version = embit.bip32.detect_version(
+                    derivation_path,
+                    default="xpub",
+                    network=embit_network
+                )
 
-            root = embit.bip32.HDKey.from_seed(
-                self.seed.seed_bytes,
-                version=embit_network["xprv"]
-            )
+                root = embit.bip32.HDKey.from_seed(
+                    self.seed.seed_bytes,
+                    version=embit_network["xprv"]
+                )
 
-            fingerprint = hexlify(root.child(0).fingerprint).decode('utf-8')
-            xprv = root.derive(derivation_path)
-            xpub = xprv.to_public()
-            xpub_base58 = xpub.to_string(version=version)
+                fingerprint = hexlify(root.child(0).fingerprint).decode('utf-8')
+                xprv = root.derive(derivation_path)
+                xpub = xprv.to_public()
+                xpub_base58 = xpub.to_string(version=version)
 
-            screen = seed_screens.SeedExportXpubDetailsScreen(
-                fingerprint=fingerprint,
-                has_passphrase=self.seed.passphrase is not None,
-                derivation_path=derivation_path,
-                xpub=xpub_base58,
-            )
+                screen = seed_screens.SeedExportXpubDetailsScreen(
+                    fingerprint=fingerprint,
+                    has_passphrase=self.seed.passphrase is not None,
+                    derivation_path=derivation_path,
+                    xpub=xpub_base58,
+                )
 
-            self.loading_screen.stop()
+                self.loading_screen.stop()
+                selected_menu_num = screen.display()
 
-            selected_menu_num = screen.display()
-        else:
+            else:
+                selected_menu_num = 0
+
+        finally:
             self.loading_screen.stop()
-            selected_menu_num = 0
+
 
         if selected_menu_num == 0:
             return Destination(
@@ -1321,7 +1325,8 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
         seeds = self.controller.storage.seeds
 
         SCAN_SEED = ("Scan a seed", FontAwesomeIconConstants.QRCODE)
-        ENTER_WORDS = "Enter 12/24 words"
+        TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
+        TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
         button_data = []
 
         text = "Load the seed to verify"
@@ -1337,7 +1342,8 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
             text = "Select seed to verify"
 
         button_data.append(SCAN_SEED)
-        button_data.append(ENTER_WORDS)
+        button_data.append(TYPE_12WORD)
+        button_data.append(TYPE_24WORD)
 
         selected_menu_num = seed_screens.SeedSingleSigAddressVerificationSelectSeedScreen(
             title="Verify Address",
@@ -1364,7 +1370,12 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
             from seedsigner.views.scan_views import ScanView
             return Destination(ScanView)
 
-        elif button_data[selected_menu_num] == ENTER_WORDS:
+        elif button_data[selected_menu_num] in [TYPE_12WORD, TYPE_24WORD]:
+            from seedsigner.views.seed_views import SeedMnemonicEntryView
+            if button_data[selected_menu_num] == TYPE_12WORD:
+                self.controller.storage.init_pending_mnemonic(num_words=12)
+            else:
+                self.controller.storage.init_pending_mnemonic(num_words=24)
             return Destination(SeedMnemonicEntryView)