From e26ce3a0e63b227ad46553a63ef0bc7254de9b84 Mon Sep 17 00:00:00 2001 From: kdmukai Date: Fri, 21 Jan 2022 17:20:38 -0600 Subject: [PATCH] Adds Settings option to enable CompactSeedQR; more test scenarios Also misc cleanup, simplifications (f-strings in `settings_tools_view.py`. --- src/default_settings.ini | 3 ++- src/seedsigner/controller.py | 10 ++++++++ src/seedsigner/helpers/path.py | 1 + src/seedsigner/models/encode_qr.py | 3 --- src/seedsigner/models/settings.py | 18 ++++++++++--- src/seedsigner/views/menu_view.py | 25 ++++++++++++------ src/seedsigner/views/seed_tools_view.py | 12 ++++++--- src/seedsigner/views/settings_tools_view.py | 28 +++++++++++++++------ src/settings.ini | 3 ++- tests/test_seedqr.py | 16 ++++++++++++ 10 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/default_settings.ini b/src/default_settings.ini index 2aeffa0d..df65b877 100644 --- a/src/default_settings.ini +++ b/src/default_settings.ini @@ -12,4 +12,5 @@ camera_rotation = 0 network = main software = Prompt qr_density = 2 -custom_derivation = m/0/0 \ No newline at end of file +custom_derivation = m/0/0 +compact_seedqr_enabled = False \ No newline at end of file diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index cf700d1c..36d07f98 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -159,6 +159,8 @@ class Controller(Singleton): ret_val = self.show_persistent_settings_tool() elif ret_val == Path.CAMERA_ROTATION: ret_val = self.show_camera_rotation_tool() + elif ret_val == Path.COMPACT_SEEDQR_ENABLED: + ret_val = self.show_compact_seedqr_enabled() elif ret_val == Path.DONATE: ret_val = self.show_donate_tool() elif ret_val == Path.RESET: @@ -1111,6 +1113,14 @@ class Controller(Singleton): return Path.SETTINGS_SUB_MENU ### Show Donate Screen and QR + + def show_compact_seedqr_enabled(self): + r = self.settings_tools_view.display_compact_seedqr_enabled() + if r is not None: + self.settings.compact_seedqr_enabled = r + + return Path.SETTINGS_SUB_MENU ### Show Donate Screen and QR + def show_donate_tool(self): self.settings_tools_view.display_donate_info_screen() diff --git a/src/seedsigner/helpers/path.py b/src/seedsigner/helpers/path.py index f641c459..b333518e 100644 --- a/src/seedsigner/helpers/path.py +++ b/src/seedsigner/helpers/path.py @@ -29,6 +29,7 @@ class Path: QR_DENSITY_SETTING = 65 PERSISTENT_SETTINGS = 67 CAMERA_ROTATION = 69 + COMPACT_SEEDQR_ENABLED = 70 RESET = 68 # Seed Slots diff --git a/src/seedsigner/models/encode_qr.py b/src/seedsigner/models/encode_qr.py index 639847b3..e8cbe6ef 100644 --- a/src/seedsigner/models/encode_qr.py +++ b/src/seedsigner/models/encode_qr.py @@ -248,9 +248,6 @@ class CompactSeedQR(SeedQR): for i in range(0, math.ceil(len(binary_str) / 8)): # int conversion reads byte data as a string prefixed with '0b' as_bytes.append(int('0b' + binary_str[i*8:(i+1)*8], 2)) - - print(f"num bytes: {len(as_bytes)}") - print(as_bytes) # Must return data as `bytes` for `qrcode` to properly recognize it as byte data return bytes(as_bytes) diff --git a/src/seedsigner/models/settings.py b/src/seedsigner/models/settings.py index 586f3040..d4559cfa 100644 --- a/src/seedsigner/models/settings.py +++ b/src/seedsigner/models/settings.py @@ -34,7 +34,8 @@ class Settings(Singleton): 'network': "main", 'software': "Specter Desktop", 'qr_density': EncodeQRDensity.MEDIUM, - 'custom_derivation': 'm/0/0' + 'custom_derivation': 'm/0/0', + 'compact_seedqr_enabled': False, } } @@ -51,12 +52,13 @@ class Settings(Singleton): self._data["system"]["debug"] = config.getboolean("system", "debug") self._data["system"]["default_language"] = config["system"]["default_language"] self._data["display"]["text_color"] = config["display"]["text_color"] + self.qr_background_color = config["display"]["qr_background_color"] self._data["display"]["camera_rotation"] = int(config["display"]["camera_rotation"]) self.network = config["wallet"]["network"] self.software = config["wallet"]["software"] self.qr_density = int(config["wallet"]["qr_density"]) self.custom_derivation = config["wallet"]["custom_derivation"] - self.qr_background_color = config["display"]["qr_background_color"] + self.compact_seedqr_enabled = config.getboolean("wallet", "compact_seedqr_enabled") ### persistent settings handling @@ -225,7 +227,17 @@ class Settings(Singleton): def custom_derivation(self, value): # TODO: parse and validate custom derivation path self._data["wallet"]["custom_derivation"] = value - + self.__writeConfig() + + @property + def compact_seedqr_enabled(self): + return self._data["wallet"]["compact_seedqr_enabled"] + + @compact_seedqr_enabled.setter + def compact_seedqr_enabled(self, value): + self._data["wallet"]["compact_seedqr_enabled"] = value + self.__writeConfig() + @staticmethod def calc_derivation(network, wallet_type, script_type): if network == "main": diff --git a/src/seedsigner/views/menu_view.py b/src/seedsigner/views/menu_view.py index 6fcbd1a9..8756312a 100644 --- a/src/seedsigner/views/menu_view.py +++ b/src/seedsigner/views/menu_view.py @@ -124,13 +124,20 @@ class MenuView(View): ### Settings Menu def display_settings_menu(self) -> int: - lines = ["... [ Return to Main ]", "Wallet: ", "Network: ", "QR Density: ", "Input / Output Tests", "Persistent Settings: ", "Camera Rotation", "Version Info", "Donate to SeedSigner", "Reset SeedSigner"] + lines = [ + "... [ Return to Main ]", + f"Wallet: {Settings.get_instance().software}", + f"Network: {Settings.get_instance().network}", + f"QR Density: {Settings.get_instance().qr_density_name}", + "Input / Output Tests", + f"Persistent Settings: {Settings.get_instance().persistent_display}", + f"Camera Rotation: {Settings.get_instance().camera_rotation}°", + f"Compact SeedQR: {'Enabled' if Settings.get_instance().compact_seedqr_enabled else 'Disabled'}", + "Version Info", + "Donate to SeedSigner", + "Reset SeedSigner" + ] input = 0 - - lines[1] = lines[1].replace("", Settings.get_instance().software) - lines[2] = lines[2].replace("", Settings.get_instance().network) - lines[3] = lines[3].replace("", Settings.get_instance().qr_density_name) - lines[5] = lines[5].replace("", Settings.get_instance().persistent_display) # Draw Menu self.selected_menu_num = 1 @@ -159,10 +166,12 @@ class MenuView(View): elif self.selected_menu_num == 7: return Path.CAMERA_ROTATION elif self.selected_menu_num == 8: - return Path.VERSION_INFO + return Path.COMPACT_SEEDQR_ENABLED elif self.selected_menu_num == 9: - return Path.DONATE + return Path.VERSION_INFO elif self.selected_menu_num == 10: + return Path.DONATE + elif self.selected_menu_num == 11: return Path.RESET raise Exception("Unhandled case") diff --git a/src/seedsigner/views/seed_tools_view.py b/src/seedsigner/views/seed_tools_view.py index c5120231..8aab8744 100644 --- a/src/seedsigner/views/seed_tools_view.py +++ b/src/seedsigner/views/seed_tools_view.py @@ -967,7 +967,10 @@ class SeedToolsView(View): elif show_qr_option and ret_val == B.KEY_RIGHT: # Show the resulting seed as a transcribable QR code - self.display_seed_qr_options(seed_phrase) + if self.controller.settings.compact_seedqr_enabled: + self.display_seed_qr_options(seed_phrase) + else: + self.seed_phrase_as_qr(seed_phrase) # Signal success to move forward return True @@ -994,7 +997,10 @@ class SeedToolsView(View): elif show_qr_option and ret_val == B.KEY_RIGHT: # Show the resulting seed as a transcribable QR code - self.display_seed_qr_options(seed_phrase) + if self.controller.settings.compact_seedqr_enabled: + self.display_seed_qr_options(seed_phrase) + else: + self.seed_phrase_as_qr(seed_phrase) # Signal success to move forward return True @@ -1025,7 +1031,7 @@ class SeedToolsView(View): return None - def seed_phrase_as_qr(self, seed_phrase, is_compact_seedqr=True): + def seed_phrase_as_qr(self, seed_phrase, is_compact_seedqr=False): if is_compact_seedqr: e = EncodeQR(seed_phrase=seed_phrase, qr_type=QRType.COMPACTSEEDQR, wordlist=self.controller.settings.wordlist) else: diff --git a/src/seedsigner/views/settings_tools_view.py b/src/seedsigner/views/settings_tools_view.py index 36cef358..b850e926 100644 --- a/src/seedsigner/views/settings_tools_view.py +++ b/src/seedsigner/views/settings_tools_view.py @@ -64,7 +64,6 @@ class SettingsToolsView(View): return None def display_persistent_settings(self) -> bool: - lines = ["... [ Return to Settings ]"] lines.append("Yes") lines.append("No") @@ -82,10 +81,10 @@ class SettingsToolsView(View): def display_camera_rotation(self) -> int: lines = ["... [ Return to Settings ]"] - lines.append("0 (default)") - lines.append("90") - lines.append("180") - lines.append("270") + lines.append("0° (default)") + lines.append("90°") + lines.append("180°") + lines.append("270°") r = self.controller.menu_view.display_generic_selection_menu(lines, "Camera Rotation") if r == 1: @@ -123,7 +122,6 @@ class SettingsToolsView(View): ### ### Custom Derivation Path ### - def draw_derivation_keyboard_entry(self, existing_derivation = "m/"): def render_right_panel(): row_height = 28 @@ -273,4 +271,20 @@ class SettingsToolsView(View): # Render the text entry display and cursor block text_entry_display.render(self.derivation) - View.DispShowImage() \ No newline at end of file + View.DispShowImage() + + + def display_compact_seedqr_enabled(self) -> bool: + lines = ["... [ Return to Settings ]"] + lines.append("Enable") + lines.append("Disable") + + r = self.controller.menu_view.display_generic_selection_menu(lines, "Compact SeedQR") + if r == 1: + return None + elif r == 2: + return True + elif r == 3: + return False + else: + return None diff --git a/src/settings.ini b/src/settings.ini index 2aeffa0d..df65b877 100644 --- a/src/settings.ini +++ b/src/settings.ini @@ -12,4 +12,5 @@ camera_rotation = 0 network = main software = Prompt qr_density = 2 -custom_derivation = m/0/0 \ No newline at end of file +custom_derivation = m/0/0 +compact_seedqr_enabled = False \ No newline at end of file diff --git a/tests/test_seedqr.py b/tests/test_seedqr.py index 63d7dc1c..5c2b7d04 100644 --- a/tests/test_seedqr.py +++ b/tests/test_seedqr.py @@ -77,6 +77,14 @@ def test_compact_seedqr_handles_null_bytes(): entropy = os.urandom(31) + b'\x00' run_encode_decode_test(entropy, mnemonic_length=24, qr_type=QRType.COMPACTSEEDQR) + # 24-word seed, multiple null bytes + entropy = os.urandom(5) + b'\x00' + os.urandom(5) + b'\x00' + os.urandom(20) + run_encode_decode_test(entropy, mnemonic_length=24, qr_type=QRType.COMPACTSEEDQR) + + # 24-word seed, multiple null bytes in a row + entropy = os.urandom(10) + b'\x00\x00' + os.urandom(20) + run_encode_decode_test(entropy, mnemonic_length=24, qr_type=QRType.COMPACTSEEDQR) + # 12-word seed, null bytes at the beginning entropy = b'\x00' + os.urandom(15) run_encode_decode_test(entropy, mnemonic_length=12, qr_type=QRType.COMPACTSEEDQR) @@ -88,3 +96,11 @@ def test_compact_seedqr_handles_null_bytes(): # 12-word seed, null bytes at the end entropy = os.urandom(15) + b'\x00' run_encode_decode_test(entropy, mnemonic_length=12, qr_type=QRType.COMPACTSEEDQR) + + # 12-word seed, multiple null bytes + entropy = os.urandom(5) + b'\x00' + os.urandom(5) + b'\x00' + os.urandom(4) + run_encode_decode_test(entropy, mnemonic_length=12, qr_type=QRType.COMPACTSEEDQR) + + # 12-word seed, multiple null bytes in a row + entropy = os.urandom(10) + b'\x00\x00' + os.urandom(4) + run_encode_decode_test(entropy, mnemonic_length=12, qr_type=QRType.COMPACTSEEDQR)