Replace the "coordinator" setting with "xpub_qr_type"
What changed, and why it matters
This commit renames a user-facing setting from picking which Bitcoin wallet app ('coordinator') you use to picking the actual QR code format you want when exporting an extended public key (xpub). It is a settings-label and workflow refactor, not a security fix or vulnerability patch. The code still supports the same three QR formats as before (animated UR, static, and the older Specter format), just selected by format name instead of wallet name.
No security action required. Treat as a normal feature/usability refactor. Reviewers may want to confirm that existing SettingsQR strings using the old 'coords=' key are migrated or still accepted, since the test still includes 'coords=spa,spd' in one settingsqr_data string while also adding 'xpub_qr=urca,sta'.
Security signals we found
No security-relevant signal in diff: refactor only
No new trust boundaries or privilege changes
No input parsing, serialization, or crypto changes beyond renaming
No vendor disclosure of security relevance in commit message or diff
Evidence from the diff
The change removes SettingsConstants.COORDINATOR__ and SETTING__COORDINATORS, replacing them with XPUB_QR_FORMAT__ and SETTING__XPUB_QR_FORMAT. The view SeedExportXpubCoordinatorView is renamed SeedExportXpubQRFormatView, and the encoder selection logic in SeedExportXpubQRDisplayView now maps xpub_qr_format values directly to StaticXpubQrEncoder, SpecterLegacyXPubQrEncoder (renamed from SpecterXPubQrEncoder), or UrXpubQrEncoder. Tests and localization strings are updated accordingly. No cryptographic, input-validation, or permission changes are present.
Changed components
src/seedsigner/models/settings_definition.pysrc/seedsigner/views/seed_views.pysrc/seedsigner/models/encode_qr.pytests/test_flows_seed.pytests/test_settings.pytests/test_controller.pytests/test_flows_settings.pytests/test_encodepsbtqr.pytests/screenshot_generator/generator.pyl10n/messages.potInspect captured patch +159 / −137
diff --git a/l10n/messages.pot b/l10n/messages.pot
index 080ca8a..b87cb99 100644
--- a/l10n/messages.pot
+++ b/l10n/messages.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: seedsigner 0.8.6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2025-11-21 14:39-0600\n"
+"POT-Creation-Date: 2025-12-14 05:46-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -597,6 +597,10 @@ msgstr ""
msgid "Next {}"
msgstr ""
+#: src/seedsigner/hardware/camera.py
+msgid "Camera error. Check camera connections."
+msgstr ""
+
#: src/seedsigner/models/settings_definition.py
msgid "Enabled"
msgstr ""
@@ -613,6 +617,21 @@ msgstr ""
msgid "Required"
msgstr ""
+#. QR code format option; "default" = this is the format most wallets use
+#: src/seedsigner/models/settings_definition.py
+msgid "Animated (default)"
+msgstr ""
+
+#. QR code format option (static = single frame, not animated)
+#: src/seedsigner/models/settings_definition.py
+msgid "Static"
+msgstr ""
+
+#. QR code format option: old format that Specter Desktop used to use
+#: src/seedsigner/models/settings_definition.py
+msgid "Specter legacy"
+msgstr ""
+
#: src/seedsigner/models/settings_definition.py
msgid "BTC"
msgstr ""
@@ -718,10 +737,6 @@ msgstr ""
msgid "Persistent settings"
msgstr ""
-#: src/seedsigner/models/settings_definition.py
-msgid "Coordinator software"
-msgstr ""
-
#: src/seedsigner/models/settings_definition.py
msgid "Denomination display"
msgstr ""
@@ -746,6 +761,10 @@ msgstr ""
msgid "Script types"
msgstr ""
+#: src/seedsigner/models/settings_definition.py
+msgid "Xpub QR format"
+msgstr ""
+
#: src/seedsigner/models/settings_definition.py
msgid "Show xpub details"
msgstr ""
@@ -853,7 +872,7 @@ msgid ""
"addresses."
msgstr ""
-#: src/seedsigner/views/psbt_views.py
+#: src/seedsigner/views/psbt_views.py src/seedsigner/views/view.py
msgid "Continue"
msgstr ""
@@ -1003,9 +1022,8 @@ msgstr ""
msgid "QRCode is invalid or is a data format not yet supported."
msgstr ""
-#: src/seedsigner/views/scan_views.py src/seedsigner/views/seed_views.py
-#: src/seedsigner/views/view.py
-msgid "Done"
+#: src/seedsigner/views/scan_views.py src/seedsigner/views/view.py
+msgid "Back to Main Menu"
msgstr ""
#. This is on the opening splash screen, displayed above the HRF logo
@@ -1074,6 +1092,10 @@ msgstr ""
msgid "Checksum failure; not a valid seed phrase."
msgstr ""
+#: src/seedsigner/views/seed_views.py
+msgid "Done"
+msgstr ""
+
#: src/seedsigner/views/seed_views.py
msgid "Edit passphrase"
msgstr ""
@@ -1159,6 +1181,10 @@ msgstr ""
msgid "Export Xpub"
msgstr ""
+#: src/seedsigner/views/seed_views.py
+msgid "Xpub QR Format"
+msgstr ""
+
#: src/seedsigner/views/seed_views.py
msgid "Xpub can be used to view all future transactions."
msgstr ""
@@ -1321,10 +1347,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
-#: src/seedsigner/views/seed_views.py
-msgid "Can't validate a single sig addr without specifying a seed"
-msgstr ""
-
#: src/seedsigner/views/seed_views.py
msgid "Return to transaction"
msgstr ""
@@ -1518,6 +1540,18 @@ msgstr ""
msgid "System Error"
msgstr ""
+#: src/seedsigner/views/view.py
+msgid "Hardware Error"
+msgstr ""
+
+#: src/seedsigner/views/view.py
+msgid "Cannot access camera"
+msgstr ""
+
+#: src/seedsigner/views/view.py
+msgid "Disconnect power and check for a loose camera connection."
+msgstr ""
+
#: src/seedsigner/views/view.py
msgid "Update setting"
msgstr ""
@@ -1532,3 +1566,13 @@ msgstr ""
msgid "Option Disabled"
msgstr ""
+#: src/seedsigner/views/view.py
+msgid "Action Required"
+msgstr ""
+
+#: src/seedsigner/views/view.py
+msgid ""
+"You must remove the\n"
+"MicroSD card to continue."
+msgstr ""
+
diff --git a/src/seedsigner/models/encode_qr.py b/src/seedsigner/models/encode_qr.py
index 37aeb78..86be8c6 100644
--- a/src/seedsigner/models/encode_qr.py
+++ b/src/seedsigner/models/encode_qr.py
@@ -238,7 +238,11 @@ class BaseSimpleAnimatedQREncoder(BaseQrEncoder):
@dataclass
-class SpecterXPubQrEncoder(BaseSimpleAnimatedQREncoder, BaseXpubQrEncoder):
+class SpecterLegacyXPubQrEncoder(BaseSimpleAnimatedQREncoder, BaseXpubQrEncoder):
+ """
+ Legacy "pXofY" format. Included here for compatibility with much older versions of
+ Specter Desktop. Can probably eventually be removed.
+ """
@property
def qr_max_fragment_size(self):
density_mapping = {
diff --git a/src/seedsigner/models/settings_definition.py b/src/seedsigner/models/settings_definition.py
index 2a6a1d7..42d6866 100644
--- a/src/seedsigner/models/settings_definition.py
+++ b/src/seedsigner/models/settings_definition.py
@@ -38,17 +38,18 @@ class SettingsConstants:
]
# User-facing selection options
- COORDINATOR__BLUE_WALLET = "bw"
- COORDINATOR__NUNCHUK = "nun"
- COORDINATOR__SPARROW = "spa"
- COORDINATOR__SPECTER_DESKTOP = "spd"
- COORDINATOR__KEEPER = "kpr"
- ALL_COORDINATORS = [
- (COORDINATOR__BLUE_WALLET, "BlueWallet"),
- (COORDINATOR__NUNCHUK, "Nunchuk"),
- (COORDINATOR__SPARROW, "Sparrow"),
- (COORDINATOR__SPECTER_DESKTOP, "Specter Desktop"),
- (COORDINATOR__KEEPER, "Keeper"),
+ XPUB_QR_FORMAT__UR_CRYPTO_ACCOUNT = "urca"
+ XPUB_QR_FORMAT__STATIC = "sta"
+ XPUB_QR_FORMAT__SPECTER_LEGACY = "spl"
+ ALL_XPUB_QR_FORMATS = [
+ # TRANSLATOR_NOTE: QR code format option; "default" = this is the format most wallets use
+ (XPUB_QR_FORMAT__UR_CRYPTO_ACCOUNT, _mft("Animated (default)")),
+
+ # TRANSLATOR_NOTE: QR code format option (static = single frame, not animated)
+ (XPUB_QR_FORMAT__STATIC, _mft("Static")),
+
+ # TRANSLATOR_NOTE: QR code format option: old format that Specter Desktop used to use
+ (XPUB_QR_FORMAT__SPECTER_LEGACY, _mft("Specter legacy")),
]
# Over-specifying current and possible future locales to reduce/eliminate main repo
@@ -326,7 +327,7 @@ class SettingsConstants:
SETTING__LOCALE = "locale"
SETTING__WORDLIST_LANGUAGE = "wordlist_language"
SETTING__PERSISTENT_SETTINGS = "persistent_settings"
- SETTING__COORDINATORS = "coordinators"
+ SETTING__XPUB_QR_FORMAT = "xpub_qr"
SETTING__BTC_DENOMINATION = "denomination"
SETTING__DISPLAY_CONFIGURATION = "display_config"
@@ -569,19 +570,6 @@ class SettingsDefinition:
help_text=SettingsConstants.PERSISTENT_SETTINGS__SD_INSERTED__HELP_TEXT,
default_value=SettingsConstants.OPTION__DISABLED),
- SettingsEntry(category=SettingsConstants.CATEGORY__WALLET,
- attr_name=SettingsConstants.SETTING__COORDINATORS,
- abbreviated_name="coords",
- display_name=_mft("Coordinator software"),
- type=SettingsConstants.TYPE__MULTISELECT,
- selection_options=SettingsConstants.ALL_COORDINATORS,
- default_value=[
- SettingsConstants.COORDINATOR__BLUE_WALLET,
- SettingsConstants.COORDINATOR__NUNCHUK,
- SettingsConstants.COORDINATOR__SPARROW,
- SettingsConstants.COORDINATOR__SPECTER_DESKTOP,
- ]),
-
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__BTC_DENOMINATION,
abbreviated_name="denom",
@@ -632,6 +620,17 @@ class SettingsDefinition:
selection_options=SettingsConstants.ALL_SCRIPT_TYPES,
default_value=[SettingsConstants.NATIVE_SEGWIT, SettingsConstants.NESTED_SEGWIT, SettingsConstants.TAPROOT]),
+ SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
+ attr_name=SettingsConstants.SETTING__XPUB_QR_FORMAT,
+ display_name=_mft("Xpub QR format"),
+ visibility=SettingsConstants.VISIBILITY__ADVANCED,
+ type=SettingsConstants.TYPE__MULTISELECT,
+ selection_options=SettingsConstants.ALL_XPUB_QR_FORMATS,
+ default_value=[
+ SettingsConstants.XPUB_QR_FORMAT__UR_CRYPTO_ACCOUNT,
+ SettingsConstants.XPUB_QR_FORMAT__STATIC,
+ ]),
+
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__XPUB_DETAILS,
display_name=_mft("Show xpub details"),
diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py
index 175bd34..a977767 100644
--- a/src/seedsigner/views/seed_views.py
+++ b/src/seedsigner/views/seed_views.py
@@ -11,7 +11,7 @@ from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconCo
from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen,
WarningScreen, DireWarningScreen, seed_screens)
from seedsigner.gui.screens.screen import ButtonOption, ButtonOptionWithoutTranslation
-from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder
+from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterLegacyXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings import Settings, SettingsConstants
@@ -720,7 +720,7 @@ class SeedExportXpubScriptTypeView(View):
del args["sig_type"]
return Destination(ToolsAddressExplorerAddressTypeView, view_args=args, skip_current_view=True)
else:
- return Destination(SeedExportXpubCoordinatorView, view_args=args, skip_current_view=True)
+ return Destination(SeedExportXpubQRFormatView, view_args=args, skip_current_view=True)
title = _("Export Xpub")
if self.controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER:
@@ -755,7 +755,7 @@ class SeedExportXpubScriptTypeView(View):
del args["sig_type"]
return Destination(ToolsAddressExplorerAddressTypeView, view_args=args)
else:
- return Destination(SeedExportXpubCoordinatorView, view_args=args)
+ return Destination(SeedExportXpubQRFormatView, view_args=args)
@@ -786,7 +786,7 @@ class SeedExportXpubCustomDerivationView(View):
return Destination(ToolsAddressExplorerAddressTypeView, view_args=dict(seed_num=self.seed_num, script_type=self.script_type, custom_derivation=custom_derivation))
return Destination(
- SeedExportXpubCoordinatorView,
+ SeedExportXpubQRFormatView,
view_args={
"seed_num": self.seed_num,
"sig_type": self.sig_type,
@@ -797,7 +797,7 @@ class SeedExportXpubCustomDerivationView(View):
-class SeedExportXpubCoordinatorView(View):
+class SeedExportXpubQRFormatView(View):
def __init__(self, seed_num: int, sig_type: str, script_type: str, custom_derivation: str = None):
super().__init__()
self.seed_num = seed_num
@@ -813,41 +813,39 @@ class SeedExportXpubCoordinatorView(View):
"script_type": self.script_type,
"custom_derivation": self.custom_derivation,
}
- if len(self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)) == 1:
+ if len(self.settings.get_value(SettingsConstants.SETTING__XPUB_QR_FORMAT)) == 1:
# Nothing to select; skip this screen
- args["coordinator"] = self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)[0]
+ args["xpub_qr_format"] = self.settings.get_value(SettingsConstants.SETTING__XPUB_QR_FORMAT)[0]
return Destination(SeedExportXpubWarningView, view_args=args, skip_current_view=True)
button_data = []
- for display_name, setting_option in zip(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS), self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)):
+ for display_name, setting_option in zip(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__XPUB_QR_FORMAT), self.settings.get_value(SettingsConstants.SETTING__XPUB_QR_FORMAT)):
button_data.append(ButtonOption(display_name, return_data=setting_option))
selected_menu_num = self.run_screen(
ButtonListScreen,
- title=_("Export Xpub"),
+ title=_("Xpub QR Format"),
is_button_text_centered=False,
button_data=button_data,
+ is_bottom_list=True,
)
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
- # coordinators_settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__COORDINATORS)
- # selected_display_name = button_data[selected_menu_num]
- # args["coordinator"] = coordinators_settings_entry.get_selection_option_value_by_display_name(selected_display_name)
- args["coordinator"] = button_data[selected_menu_num].return_data
+ args["xpub_qr_format"] = button_data[selected_menu_num].return_data
return Destination(SeedExportXpubWarningView, view_args=args)
class SeedExportXpubWarningView(View):
- def __init__(self, seed_num: int, sig_type: str, script_type: str, coordinator: str, custom_derivation: str):
+ def __init__(self, seed_num: int, sig_type: str, script_type: str, xpub_qr_format: str, custom_derivation: str):
super().__init__()
self.seed_num = seed_num
self.sig_type = sig_type
self.script_type = script_type
- self.coordinator = coordinator
+ self.xpub_qr_format = xpub_qr_format
self.custom_derivation = custom_derivation
@@ -858,7 +856,7 @@ class SeedExportXpubWarningView(View):
"seed_num": self.seed_num,
"sig_type": self.sig_type,
"script_type": self.script_type,
- "coordinator": self.coordinator,
+ "xpub_qr_format": self.xpub_qr_format,
"custom_derivation": self.custom_derivation,
},
skip_current_view=True, # Prevent going BACK to WarningViews
@@ -888,11 +886,11 @@ 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, coordinator: str, custom_derivation: str):
+ def __init__(self, seed_num: int, 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.coordinator = coordinator
+ self.xpub_qr_format = xpub_qr_format
self.custom_derivation = custom_derivation
self.seed_num = seed_num
@@ -955,7 +953,7 @@ class SeedExportXpubDetailsView(View):
return Destination(
SeedExportXpubQRDisplayView,
dict(seed_num=self.seed_num,
- coordinator=self.coordinator,
+ xpub_qr_format=self.xpub_qr_format,
derivation_path=derivation_path,
sig_type=self.sig_type
)
@@ -967,7 +965,7 @@ class SeedExportXpubDetailsView(View):
class SeedExportXpubQRDisplayView(View):
- def __init__(self, seed_num: int, coordinator: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG):
+ def __init__(self, seed_num: int, xpub_qr_format: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG):
super().__init__()
self.seed = self.controller.get_seed(seed_num)
@@ -979,14 +977,14 @@ class SeedExportXpubQRDisplayView(View):
sig_type=sig_type
)
- if coordinator == SettingsConstants.COORDINATOR__SPECTER_DESKTOP:
- self.qr_encoder = SpecterXPubQrEncoder(**encoder_args)
-
- elif coordinator in [SettingsConstants.COORDINATOR__BLUE_WALLET,
- SettingsConstants.COORDINATOR__KEEPER]:
+ if xpub_qr_format == SettingsConstants.XPUB_QR_FORMAT__STATIC:
self.qr_encoder = StaticXpubQrEncoder(**encoder_args)
+ elif xpub_qr_format == SettingsConstants.XPUB_QR_FORMAT__SPECTER_LEGACY:
+ self.qr_encoder = SpecterLegacyXPubQrEncoder(**encoder_args)
+
else:
+ # Default: UR crypto-address
self.qr_encoder = UrXpubQrEncoder(**encoder_args)
diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py
index e45373f..209fb32 100644
--- a/tests/screenshot_generator/generator.py
+++ b/tests/screenshot_generator/generator.py
@@ -237,8 +237,8 @@ def generate_screenshots(locale):
)
add_settings_entries(SettingsConstants.VISIBILITY__HARDWARE)
- settingsqr_data_persistent = f"settings::v1 name=English_noob_mode persistent=E coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
- settingsqr_data_not_persistent = f"settings::v1 name=Mode_Ephemeral persistent=D coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
+ settingsqr_data_persistent = f"settings::v1 name=English_noob_mode persistent=E xpub_qr=urca,sta denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
+ settingsqr_data_not_persistent = f"settings::v1 name=Mode_Ephemeral persistent=D xpub_qr=urca,sta denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
# Set up screenshot-specific callbacks to inject data before the View is run and
# reset data after the View is run.
@@ -334,10 +334,10 @@ def generate_screenshots(locale):
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.SeedExportXpubCoordinatorView, dict(seed_num=0, sig_type="ss", script_type="nat")),
- ScreenshotConfig(seed_views.SeedExportXpubWarningView, dict(seed_num=0, sig_type="msig", script_type="nes", coordinator="spd", custom_derivation="")),
- ScreenshotConfig(seed_views.SeedExportXpubDetailsView, dict(seed_num=0, sig_type="ss", script_type="nat", coordinator="bw", custom_derivation="")),
- ScreenshotConfig(SeedExportXpubQR_ScreenBrightnessView, dict(seed_num=0, coordinator="bw", derivation_path="m/84'/0'/0'")),
+ 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)),
diff --git a/tests/test_controller.py b/tests/test_controller.py
index 0be99d0..f414dc8 100644
--- a/tests/test_controller.py
+++ b/tests/test_controller.py
@@ -4,7 +4,6 @@ import pytest
from base import BaseTest
from seedsigner.controller import Controller
-from seedsigner.models.settings_definition import SettingsConstants
class TestController(BaseTest):
@@ -80,38 +79,3 @@ class TestController(BaseTest):
# ...get a new copy of the instance and confirm change
controller = Controller.get_instance()
assert controller.unverified_address == "123abc"
-
-
- def test_missing_settings_get_defaults(self):
- """ Should gracefully handle all missing fields from `settings.json` """
-
- controller = Controller.get_instance()
-
- # Settings defaults
- assert controller.settings.get_value(SettingsConstants.SETTING__LOCALE) == SettingsConstants.LOCALE__ENGLISH
- assert controller.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE) == SettingsConstants.WORDLIST_LANGUAGE__ENGLISH
- assert controller.settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == SettingsConstants.OPTION__DISABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__COORDINATORS) == [i for i,j in SettingsConstants.ALL_COORDINATORS if i!="kpr"]
- assert controller.settings.get_value(SettingsConstants.SETTING__BTC_DENOMINATION) == SettingsConstants.BTC_DENOMINATION__THRESHOLD
-
- # Advanced Settings defaults
- assert controller.settings.get_value(SettingsConstants.SETTING__NETWORK) == SettingsConstants.MAINNET
- assert controller.settings.get_value(SettingsConstants.SETTING__QR_DENSITY) == SettingsConstants.DENSITY__MEDIUM
- assert controller.settings.get_value(SettingsConstants.SETTING__XPUB_EXPORT) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__SIG_TYPES) == [i for i,j in SettingsConstants.ALL_SIG_TYPES]
- assert controller.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES) == [SettingsConstants.NATIVE_SEGWIT, SettingsConstants.NESTED_SEGWIT, SettingsConstants.TAPROOT]
- assert controller.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__PASSPHRASE) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__CAMERA_ROTATION) == SettingsConstants.CAMERA_ROTATION__180
- assert controller.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__BIP85_CHILD_SEEDS) == SettingsConstants.OPTION__DISABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__MESSAGE_SIGNING) == SettingsConstants.OPTION__DISABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__PRIVACY_WARNINGS) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__DIRE_WARNINGS) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__QR_BRIGHTNESS_TIPS) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__PARTNER_LOGOS) == SettingsConstants.OPTION__ENABLED
- assert controller.settings.get_value(SettingsConstants.SETTING__MICROSD_TOAST_TIMER) == SettingsConstants.MICROSD_TOAST_TIMER_FIVE_SECONDS
-
- # Hidden Settings defaults
- assert controller.settings.get_value(SettingsConstants.SETTING__QR_BRIGHTNESS) == 62
-
diff --git a/tests/test_encodepsbtqr.py b/tests/test_encodepsbtqr.py
index 2d864c1..3b33c0c 100644
--- a/tests/test_encodepsbtqr.py
+++ b/tests/test_encodepsbtqr.py
@@ -1,4 +1,4 @@
-from seedsigner.models.encode_qr import CompactSeedQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrPsbtQrEncoder, UrXpubQrEncoder
+from seedsigner.models.encode_qr import CompactSeedQrEncoder, SeedQrEncoder, SpecterLegacyXPubQrEncoder, StaticXpubQrEncoder, UrPsbtQrEncoder, UrXpubQrEncoder
from embit import psbt
from binascii import a2b_base64
@@ -54,7 +54,7 @@ def test_xpub_qr():
def test_specter_xpub_qr():
mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash"
- e = SpecterXPubQrEncoder(seed=Seed(mnemonic.split(" "), passphrase="pass"), network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h", qr_density=SettingsConstants.DENSITY__LOW)
+ e = SpecterLegacyXPubQrEncoder(seed=Seed(mnemonic.split(" "), passphrase="pass"), network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h", qr_density=SettingsConstants.DENSITY__LOW)
assert e.next_part() == "p1of4 [c49122a5/48h/1h/0h/2h]Vpub5mXgECaX5yYDN"
assert e.next_part() == "p2of4 c5VnUG4jVNptyEg65qUjuofWchQeuMWWiq8rcPBo"
diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py
index 2b2339f..29fe379 100644
--- a/tests/test_flows_seed.py
+++ b/tests/test_flows_seed.py
@@ -195,7 +195,7 @@ class TestSeedFlows(FlowTest):
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_selection),
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=ButtonOption(script_tuple[1], return_data=script_tuple[0])),
- FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=ButtonOption(coord_tuple[1], return_data=coord_tuple[0])),
+ FlowStep(seed_views.SeedExportXpubQRFormatView, button_data_selection=ButtonOption(coord_tuple[1], return_data=coord_tuple[0])),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
@@ -211,17 +211,17 @@ class TestSeedFlows(FlowTest):
# these are lists of (constant_value, display_name) tuples
sig_types: list[tuple[str, str]] = SettingsConstants.ALL_SIG_TYPES
script_types: list[tuple[str, str]] = SettingsConstants.ALL_SCRIPT_TYPES
- coordinators: list[tuple[str, str]] = SettingsConstants.ALL_COORDINATORS
+ xpub_qr_formats: list[tuple[str, str]] = SettingsConstants.ALL_XPUB_QR_FORMATS
# enable non-defaults so they're available in views
self.settings.set_value(SettingsConstants.SETTING__SIG_TYPES, [x for x,y in sig_types])
self.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [x for x,y in script_types])
- self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x,y in coordinators])
+ self.settings.set_value(SettingsConstants.SETTING__XPUB_QR_FORMAT, [x for x,y in xpub_qr_formats])
- # exhaustively test flows thru standard sig_types, script_types, and coordinators
+ # exhaustively test flows thru standard sig_types, script_types, and xpub_qr_formats
for sig_tuple in sig_types:
for script_tuple in script_types:
- for coord_tuple in coordinators:
+ for coord_tuple in xpub_qr_formats:
# skip custom derivation
if script_tuple[0] == SettingsConstants.CUSTOM_DERIVATION:
continue
@@ -245,17 +245,17 @@ class TestSeedFlows(FlowTest):
# these are lists of (constant_value, display_name) tuples
sig_types: list[tuple[str, str]] = SettingsConstants.ALL_SIG_TYPES
script_types: list[tuple[str, str]] = SettingsConstants.ALL_SCRIPT_TYPES
- coordinators: list[tuple[str, str]] = SettingsConstants.ALL_COORDINATORS
+ xpub_qr_formats: list[tuple[str, str]] = SettingsConstants.ALL_XPUB_QR_FORMATS
# these are the disabled types that we will be testing
disabled_sig = SettingsConstants.MULTISIG
disabled_script = SettingsConstants.TAPROOT
- disabled_coord = SettingsConstants.COORDINATOR__NUNCHUK
+ disabled_xpub_qr_format = SettingsConstants.XPUB_QR_FORMAT__SPECTER_LEGACY
# enable all but our target disabled type
self.settings.set_value(SettingsConstants.SETTING__SIG_TYPES, [x for x,y in sig_types if x!=disabled_sig])
self.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [x for x,y in script_types if x!=disabled_script])
- self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x,y in coordinators if x!=disabled_coord])
+ self.settings.set_value(SettingsConstants.SETTING__XPUB_QR_FORMAT, [x for x,y in xpub_qr_formats if x!=disabled_xpub_qr_format])
# If multisig isn't an option, then the sig type selection is skipped altogether
self.run_sequence(
@@ -286,7 +286,7 @@ class TestSeedFlows(FlowTest):
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True),
FlowStep(seed_views.SeedExportXpubScriptTypeView, screen_return_value=0),
- FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=disabled_coord),
+ FlowStep(seed_views.SeedExportXpubQRFormatView, button_data_selection=disabled_xpub_qr_format),
]
)
@@ -307,8 +307,8 @@ class TestSeedFlows(FlowTest):
SettingsConstants.CUSTOM_DERIVATION
])
- # Ensure that all coordinators are enabled
- self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x, y in SettingsConstants.ALL_COORDINATORS])
+ # Ensure that all xpub_qr_formats are enabled
+ self.settings.set_value(SettingsConstants.SETTING__XPUB_QR_FORMAT, [x for x, y in SettingsConstants.ALL_XPUB_QR_FORMATS])
# Set up button_data selections
sig_type = seed_views.SeedExportXpubSigTypeView.SINGLE_SIG
@@ -316,9 +316,9 @@ class TestSeedFlows(FlowTest):
custom_derivation = SettingsConstants.CUSTOM_DERIVATION
script_type = ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__SCRIPT_TYPES)[2], return_data=custom_derivation)
- specter = SettingsConstants.COORDINATOR__SPECTER_DESKTOP
- assert SettingsConstants.ALL_COORDINATORS[3][0] == specter
- coordinator = ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[3], return_data=specter)
+ specter_legacy = SettingsConstants.XPUB_QR_FORMAT__SPECTER_LEGACY
+ assert SettingsConstants.ALL_XPUB_QR_FORMATS[2][0] == specter_legacy
+ 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),
@@ -327,7 +327,7 @@ class TestSeedFlows(FlowTest):
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=sig_type),
FlowStep(seed_views.SeedExportXpubScriptTypeView, button_data_selection=script_type),
FlowStep(seed_views.SeedExportXpubCustomDerivationView, screen_return_value="m/0'/0'"),
- FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=coordinator),
+ FlowStep(seed_views.SeedExportXpubQRFormatView, button_data_selection=xpub_qr_format),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
@@ -338,18 +338,18 @@ class TestSeedFlows(FlowTest):
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 coordinators
+ 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))
self.controller.storage.finalize_pending_seed()
- # exclusively set only one choice for each of sig_types, script_types and coordinators
+ # exclusively set only one choice for each of sig_types, script_types and xpub_qr_formats
self.settings.update({
SettingsConstants.SETTING__SIG_TYPES: SettingsConstants.MULTISIG,
SettingsConstants.SETTING__SCRIPT_TYPES: SettingsConstants.NESTED_SEGWIT,
- SettingsConstants.SETTING__COORDINATORS: SettingsConstants.COORDINATOR__SPECTER_DESKTOP,
+ SettingsConstants.SETTING__XPUB_QR_FORMAT: SettingsConstants.XPUB_QR_FORMAT__UR_CRYPTO_ACCOUNT,
})
self.run_sequence(
@@ -358,7 +358,7 @@ class TestSeedFlows(FlowTest):
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True),
FlowStep(seed_views.SeedExportXpubScriptTypeView, is_redirect=True),
- FlowStep(seed_views.SeedExportXpubCoordinatorView, is_redirect=True),
+ FlowStep(seed_views.SeedExportXpubQRFormatView, is_redirect=True),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
@@ -379,7 +379,7 @@ class TestSeedFlows(FlowTest):
# Make sure all options are enabled
self.settings.set_value(SettingsConstants.SETTING__SIG_TYPES, [x for x,y in SettingsConstants.ALL_SIG_TYPES])
self.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [x for x,y in SettingsConstants.ALL_SCRIPT_TYPES])
- self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x,y in SettingsConstants.ALL_COORDINATORS])
+ 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),
@@ -389,7 +389,7 @@ class TestSeedFlows(FlowTest):
# Skips past the script type options via redirect
FlowStep(seed_views.SeedExportXpubScriptTypeView, is_redirect=True),
- FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[0], return_data=SettingsConstants.ALL_COORDINATORS[0][0])),
+ FlowStep(seed_views.SeedExportXpubQRFormatView, button_data_selection=ButtonOption(self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__XPUB_QR_FORMAT)[0], return_data=SettingsConstants.ALL_XPUB_QR_FORMATS[0][0])),
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
diff --git a/tests/test_flows_settings.py b/tests/test_flows_settings.py
index 37adf00..ac2381a 100644
--- a/tests/test_flows_settings.py
+++ b/tests/test_flows_settings.py
@@ -39,10 +39,11 @@ class TestSettingsFlows(FlowTest):
def test_multiselect(self):
""" Multiselect Settings options should stay in-place; requires BACK to exit. """
# Which option are we testing?
- settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__COORDINATORS)
+ settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__XPUB_QR_FORMAT)
self.run_sequence([
FlowStep(MainMenuView, button_data_selection=MainMenuView.SETTINGS),
+ FlowStep(settings_views.SettingsMenuView, button_data_selection=settings_views.SettingsMenuView.ADVANCED),
FlowStep(settings_views.SettingsMenuView, button_data_selection=ButtonOption(settings_entry.display_name)),
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=0), # select/deselect first option
FlowStep(settings_views.SettingsEntryUpdateSelectionView, screen_return_value=1), # select/deselect second option
diff --git a/tests/test_settings.py b/tests/test_settings.py
index 94ac626..7b4a3cf 100644
--- a/tests/test_settings.py
+++ b/tests/test_settings.py
@@ -1,7 +1,7 @@
import pytest
from base import BaseTest
from seedsigner.models.settings import InvalidSettingsQRData, Settings
-from seedsigner.models.settings_definition import SettingsConstants
+from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition
@@ -14,6 +14,10 @@ class TestSettings(BaseTest):
def test_reset_settings(self):
""" BaseTest.reset_settings() should wipe out any previous Settings changes """
+ settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__PERSISTENT_SETTINGS)
+ assert settings_entry.default_value == SettingsConstants.OPTION__DISABLED
+
+ # Change the setting from its default
settings = Settings.get_instance()
settings.set_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS, SettingsConstants.OPTION__ENABLED)
assert settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == SettingsConstants.OPTION__ENABLED
@@ -23,18 +27,26 @@ class TestSettings(BaseTest):
assert settings.get_value(SettingsConstants.SETTING__PERSISTENT_SETTINGS) == SettingsConstants.OPTION__DISABLED
+ def test_settings_defaults(self):
+ """ Settings should initialize to their default values """
+ BaseTest.reset_settings()
+ settings = Settings.get_instance()
+ for settings_entry in SettingsDefinition.get_settings_entries():
+ assert settings.get_value(settings_entry.attr_name) == settings_entry.default_value
+
+
def test_parse_settingsqr_data(self):
"""
SettingsQR parser should successfully parse a valid settingsqr input string and
return the resulting config_name and formatted settings_update_dict.
"""
settings_name = "Test SettingsQR"
- settingsqr_data = f"""settings::v1 name={ settings_name.replace(" ", "_") } persistent=D coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss,ms scripts=nat,nes,tr xpub_details=E passphrase=E camera=180 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E"""
+ settingsqr_data = f"""settings::v1 name={ settings_name.replace(" ", "_") } persistent=D coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss,ms scripts=nat,nes,tr xpub_qr=urca,sta xpub_details=E passphrase=E camera=180 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E"""
# First explicitly set settings that differ from the settingsqr_data
self.settings.set_value(SettingsConstants.SETTING__COMPACT_SEEDQR, SettingsConstants.OPTION__DISABLED)
self.settings.set_value(SettingsConstants.SETTING__DIRE_WARNINGS, SettingsConstants.OPTION__DISABLED)
- self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [SettingsConstants.COORDINATOR__BLUE_WALLET, SettingsConstants.COORDINATOR__SPARROW])
+ self.settings.set_value(SettingsConstants.SETTING__XPUB_QR_FORMAT, [SettingsConstants.XPUB_QR_FORMAT__STATIC, SettingsConstants.XPUB_QR_FORMAT__SPECTER_LEGACY])
# Now parse the settingsqr_data
config_name, settings_update_dict = Settings.parse_settingsqr(settingsqr_data)
@@ -45,10 +57,10 @@ class TestSettings(BaseTest):
assert self.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) == SettingsConstants.OPTION__ENABLED
assert self.settings.get_value(SettingsConstants.SETTING__DIRE_WARNINGS) == SettingsConstants.OPTION__ENABLED
- coordinators = self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)
- assert SettingsConstants.COORDINATOR__BLUE_WALLET not in coordinators
- assert SettingsConstants.COORDINATOR__SPARROW in coordinators
- assert SettingsConstants.COORDINATOR__SPECTER_DESKTOP in coordinators
+ xpub_qr_formats = self.settings.get_value(SettingsConstants.SETTING__XPUB_QR_FORMAT)
+ assert SettingsConstants.XPUB_QR_FORMAT__UR_CRYPTO_ACCOUNT in xpub_qr_formats
+ assert SettingsConstants.XPUB_QR_FORMAT__STATIC in xpub_qr_formats
+ assert SettingsConstants.XPUB_QR_FORMAT__SPECTER_LEGACY not in xpub_qr_formats
def test_settingsqr_version(self):
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.