diff --git a/docs/getting-started/usage/navigating-the-main-menu.en.md b/docs/getting-started/usage/navigating-the-main-menu.en.md index 94dff420..23998c7d 100644 --- a/docs/getting-started/usage/navigating-the-main-menu.en.md +++ b/docs/getting-started/usage/navigating-the-main-menu.en.md @@ -114,7 +114,7 @@ This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) ### Extended Public Key -A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on the script type or whether a single-sig or multisig wallet was loaded, the options shown will be xPub, yPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached. +A menu will be presented with options to display your master extended public key (xpub) as text and as a QR code. Depending on the script type or whether a single-sig or multisig wallet was loaded, the options shown will be xpub, ypub, zpub, or Zpub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached. diff --git a/pyproject.toml b/pyproject.toml index 612be5d0..b890b047 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ [tool.poetry] name = "krux" -version = "24.07.0" +version = "24.09.beta0" description = "Open-source signing device firmware for Bitcoin" authors = ["Jeff S "] diff --git a/src/krux/encryption.py b/src/krux/encryption.py index 7e070858..2703b7c8 100644 --- a/src/krux/encryption.py +++ b/src/krux/encryption.py @@ -28,7 +28,7 @@ import ucryptolib from .baseconv import base_encode, base_decode from .sd_card import SDHandler -from .krux_settings import Settings, PBKDF2_HMAC_ECB, PBKDF2_HMAC_CBC, AES_BLOCK_SIZE +from .krux_settings import Settings, PBKDF2_HMAC_ECB, PBKDF2_HMAC_CBC from embit.wordlists.bip39 import WORDLIST @@ -47,6 +47,7 @@ "AES-CBC": PBKDF2_HMAC_CBC, } +AES_BLOCK_SIZE = 16 QR_CODE_ITER_MULTIPLE = 10000 diff --git a/src/krux/key.py b/src/krux/key.py index 41ef6561..2a396e93 100644 --- a/src/krux/key.py +++ b/src/krux/key.py @@ -65,6 +65,13 @@ # address starts with bc1p (mainnet) or tb1p (testnet) P2TR = "p2tr" +SCRIPT_LONG_NAMES = { + "Legacy - 44": P2PKH, + "Nested Segwit - 49": P2SH_P2WPKH, + "Native Segwit - 84": P2WPKH, + "Taproot - 86": P2TR, +} + SINGLESIG_SCRIPT_PURPOSE = { P2PKH: 44, P2SH_P2WPKH: 49, diff --git a/src/krux/krux_settings.py b/src/krux/krux_settings.py index b171eb3b..03e72a70 100644 --- a/src/krux/krux_settings.py +++ b/src/krux/krux_settings.py @@ -19,7 +19,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. - +import board +import binascii from .settings import ( SettingsNamespace, CategorySetting, @@ -29,9 +30,8 @@ MAIN_TXT, TEST_TXT, ) -import board -import binascii from .translations import translation_table +from .key import SCRIPT_LONG_NAMES BAUDRATES = [1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200] @@ -50,7 +50,6 @@ # Encription Versions PBKDF2_HMAC_ECB = 0 PBKDF2_HMAC_CBC = 1 -AES_BLOCK_SIZE = 16 THERMAL_ADAFRUIT_TXT = "thermal/adafruit" @@ -77,12 +76,16 @@ class DefaultWallet(SettingsNamespace): namespace = "settings.wallet" network = CategorySetting("network", MAIN_TXT, [MAIN_TXT, TEST_TXT]) multisig = CategorySetting("multisig", False, [False, True]) + script_type = CategorySetting( + "script_type", "Native Segwit - 84", list(SCRIPT_LONG_NAMES.keys()) + ) def label(self, attr): """Returns a label for UI when given a setting name or namespace""" return { "network": t("Network"), "multisig": t("Multisig"), + "script_type": t("Script Type"), }[attr] @@ -242,10 +245,10 @@ def label(self, attr): }[attr] -class AmgDisplaySettings(SettingsNamespace): +class DisplayAmgSettings(SettingsNamespace): """Custom display settings for Maix Amigo""" - namespace = "settings.amg_display" + namespace = "settings.display_amg" flipped_x_coordinates = CategorySetting("flipped_x", True, [False, True]) inverted_colors = CategorySetting("inverted_colors", True, [False, True]) bgr_colors = CategorySetting("bgr_colors", True, [False, True]) @@ -288,7 +291,7 @@ def __init__(self): if board.config["type"] in ["amigo", "yahboom"]: self.touch = TouchSettings() if board.config["type"] == "amigo": - self.display = AmgDisplaySettings() + self.display = DisplayAmgSettings() elif board.config["type"] in ["cube", "m5stickv"]: self.display = DisplaySettings() @@ -302,7 +305,7 @@ def label(self, attr): if board.config["type"] in ["amigo", "yahboom"]: hardware_menu["touchscreen"] = t("Touchscreen") if board.config["type"] == "amigo": - hardware_menu["amg_display"] = t("Display") + hardware_menu["display_amg"] = t("Display") elif board.config["type"] in ["cube", "m5stickv"]: hardware_menu["display"] = t("Display") diff --git a/src/krux/metadata.py b/src/krux/metadata.py index 5a86535c..24775d79 100644 --- a/src/krux/metadata.py +++ b/src/krux/metadata.py @@ -19,5 +19,5 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -VERSION = "24.07.0" +VERSION = "24.09.beta0" SIGNER_PUBKEY = "03339e883157e45891e61ca9df4cd3bb895ef32d475b8e793559ea10a36766689b" diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index 16d1631d..dc94d44f 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -56,7 +56,6 @@ FIXED_KEYS = 3 # 'More' key only appears when there are multiple keysets ANTI_GLARE_WAIT_TIME = 500 -QR_CODE_STEP_TIME = 100 SHUTDOWN_WAIT_TIME = 300 TOGGLE_BRIGHTNESS = (BUTTON_PAGE, BUTTON_PAGE_PREV) @@ -109,7 +108,7 @@ def load_method(self): t("Load from SD card"), None if not self.has_sd_card() else lambda: None, ), - (t("Back"), lambda: None), + cta_back(lambda: None), ], ) index, _ = load_menu.run_loop() @@ -266,7 +265,6 @@ def callback(part_total, num_parts_captured, new_part): 15, theme.fg_color, ) - time.sleep_ms(QR_CODE_STEP_TIME) self.ctx.display.to_landscape() return 0 @@ -405,9 +403,9 @@ def prompt(self, text, offset_y=0): self.x_keypad_map.append(DEFAULT_PADDING) self.x_keypad_map.append(self.ctx.display.width() // 2) self.x_keypad_map.append(self.ctx.display.width() - DEFAULT_PADDING) - y_key_map = offset_y - FONT_HEIGHT // 2 + y_key_map = offset_y - (3 * FONT_HEIGHT // 2) self.y_keypad_map.append(y_key_map) - y_key_map += 2 * FONT_HEIGHT + y_key_map += 4 * FONT_HEIGHT self.y_keypad_map.append(y_key_map) if self.ctx.input.touch is not None: self.ctx.input.touch.clear_regions() @@ -448,9 +446,9 @@ def prompt(self, text, offset_y=0): for region in self.x_keypad_map: self.ctx.display.draw_line( region, - self.y_keypad_map[0], + self.y_keypad_map[0] + FONT_HEIGHT, region, - self.y_keypad_map[0] + 2 * FONT_HEIGHT, + self.y_keypad_map[0] + 3 * FONT_HEIGHT, theme.frame_color, ) btn = self.ctx.input.wait_for_button() @@ -802,6 +800,8 @@ def _draw_touch_menu(self, selected_item_index): Page.y_keypad_map = [ int(n * height_multiplier) + self.menu_offset for n in Page.y_keypad_map ] + # Expand last region to the bottom of the screen + Page.y_keypad_map[-1] = self.ctx.display.height() self.ctx.input.touch.y_regions = Page.y_keypad_map # draw dividers and outline @@ -816,6 +816,9 @@ def _draw_touch_menu(self, selected_item_index): menu_item_lines = self.ctx.display.to_lines(menu_item[0]) offset_y = Page.y_keypad_map[i + 1] - Page.y_keypad_map[i] offset_y -= len(menu_item_lines) * FONT_HEIGHT + if i == len(self.menu_view) - 1: + # Compensate for the expanded last region + offset_y -= DEFAULT_PADDING offset_y //= 2 offset_y += Page.y_keypad_map[i] fg_color = ( @@ -902,15 +905,16 @@ def choose_len_mnemonic(ctx): submenu = Menu( ctx, [ - (t("12 words"), lambda: MENU_EXIT), - (t("24 words"), lambda: MENU_EXIT), - (t("Back"), lambda: MENU_EXIT), + (t("12 words"), lambda: 12), + (t("24 words"), lambda: 24), + cta_back(lambda: None), ], ) - index, _ = submenu.run_loop() + _, num_words = submenu.run_loop() ctx.display.clear() - if index == 0: - return 12 - if index == 1: - return 24 - return None + return num_words + + +def cta_back(status=lambda: MENU_EXIT, label=t("Back")): + """Reusable 'call-to-action: go back'. Currently a menu item tuple""" + return ("< " + label, status) diff --git a/src/krux/pages/encryption_ui.py b/src/krux/pages/encryption_ui.py index a78945e0..8c73803a 100644 --- a/src/krux/pages/encryption_ui.py +++ b/src/krux/pages/encryption_ui.py @@ -21,7 +21,8 @@ # THE SOFTWARE. from ..display import BOTTOM_PROMPT_LINE -from ..krux_settings import t, Settings, AES_BLOCK_SIZE +from ..krux_settings import t, Settings +from ..encryption import AES_BLOCK_SIZE from . import ( Page, Menu, diff --git a/src/krux/pages/file_manager.py b/src/krux/pages/file_manager.py index 6248fab8..27f6590a 100644 --- a/src/krux/pages/file_manager.py +++ b/src/krux/pages/file_manager.py @@ -22,7 +22,7 @@ import board import gc -from . import Page, Menu, MENU_EXIT, MENU_CONTINUE +from . import Page, Menu, MENU_EXIT, MENU_CONTINUE, cta_back from ..sd_card import SDHandler from ..krux_settings import t from ..format import generate_thousands_separator, render_decimal_separator @@ -119,7 +119,7 @@ def select_file( # We need to add this option because /sd can be empty! items.append("Back") - menu_items.append((t("Back"), lambda: MENU_EXIT)) + menu_items.append(cta_back()) submenu = Menu(self.ctx, menu_items) index, _ = submenu.run_loop() diff --git a/src/krux/pages/home_pages/addresses.py b/src/krux/pages/home_pages/addresses.py index 9268ac97..55dc3982 100644 --- a/src/krux/pages/home_pages/addresses.py +++ b/src/krux/pages/home_pages/addresses.py @@ -29,6 +29,7 @@ Menu, MENU_CONTINUE, MENU_EXIT, + cta_back, ) SCAN_ADDRESS_LIMIT = 50 @@ -50,7 +51,7 @@ def addresses_menu(self): (t("Scan Address"), self.pre_scan_address), (t("Receive Addresses"), self.list_address_type), (t("Change Addresses"), lambda: self.list_address_type(1)), - (t("Back"), lambda: None), + cta_back(), ], ) submenu.run_loop() @@ -102,7 +103,7 @@ def list_address_type(self, addr_type=0): lambda: MENU_EXIT, ) ) - items.append((t("Back"), lambda: MENU_EXIT)) + items.append(cta_back()) submenu = Menu(self.ctx, items) stay_on_this_addr_menu = True @@ -137,7 +138,7 @@ def pre_scan_address(self): [ (t("Receive"), self.scan_address), (t("Change"), lambda: self.scan_address(1)), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() diff --git a/src/krux/pages/home_pages/home.py b/src/krux/pages/home_pages/home.py index 66fe985e..44bbe376 100644 --- a/src/krux/pages/home_pages/home.py +++ b/src/krux/pages/home_pages/home.py @@ -29,10 +29,10 @@ Page, Menu, MENU_CONTINUE, - MENU_EXIT, ESC_KEY, LOAD_FROM_CAMERA, LOAD_FROM_SD, + cta_back, ) MAX_POLICY_COSIGNERS_DISPLAYED = 5 @@ -169,7 +169,7 @@ def wallet(self): (t("Passphrase"), self.passphrase), (t("Customize"), self.customize), ("BIP85", self.bip85), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() @@ -189,7 +189,7 @@ def sign(self): [ ("PSBT", self.sign_psbt), (t("Message"), self.sign_message), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() @@ -228,7 +228,7 @@ def _sign_menu(self): t("Sign to SD card"), None if not self.has_sd_card() else lambda: None, ), - (t("Back"), lambda: None), + cta_back(lambda: None), ], ) index, _ = sign_menu.run_loop() diff --git a/src/krux/pages/home_pages/mnemonic_backup.py b/src/krux/pages/home_pages/mnemonic_backup.py index beb5033b..1bddccfb 100644 --- a/src/krux/pages/home_pages/mnemonic_backup.py +++ b/src/krux/pages/home_pages/mnemonic_backup.py @@ -27,7 +27,7 @@ Page, Menu, MENU_CONTINUE, - MENU_EXIT, + cta_back, ) @@ -42,7 +42,7 @@ def mnemonic(self): (t("QR Code"), self.qr_code_backup), (t("Encrypted"), self.encrypt_mnemonic_menu), (t("Other Formats"), self.other_backup_formats), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() @@ -57,7 +57,7 @@ def qr_code_backup(self): ("Compact SeedQR", lambda: self.display_seed_qr(True)), ("SeedQR", self.display_seed_qr), (t("Encrypted QR Code"), self.encrypt_qr_code), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() @@ -77,7 +77,7 @@ def other_backup_formats(self): (t("Numbers"), self.display_mnemonic_numbers), ("Stackbit 1248", self.stackbit), ("Tiny Seed", self.tiny_seed), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() @@ -149,7 +149,7 @@ def display_mnemonic_numbers(self): Utils.BASE_OCT_SUFFIX, ), ), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) submenu.run_loop() diff --git a/src/krux/pages/home_pages/pub_key_view.py b/src/krux/pages/home_pages/pub_key_view.py index 847d5a35..4e908abb 100644 --- a/src/krux/pages/home_pages/pub_key_view.py +++ b/src/krux/pages/home_pages/pub_key_view.py @@ -27,6 +27,7 @@ Menu, MENU_CONTINUE, MENU_EXIT, + cta_back, ) from ...sd_card import PUBKEY_FILE_EXTENSION from ...key import P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH @@ -68,7 +69,7 @@ def _pub_key_text(version): else lambda: _save_xpub_to_sd(version) ), ), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ] full_pub_key = self.ctx.wallet.key.account_pubkey_str(version) menu_offset = 5 + len(self.ctx.display.to_lines(full_pub_key)) @@ -116,7 +117,7 @@ def _pub_key_qr(version): pub_key_menu_items.append( (title + " - " + t("QR Code"), lambda ver=version: _pub_key_qr(ver)) ) - pub_key_menu_items.append((t("Back"), lambda: MENU_EXIT)) + pub_key_menu_items.append(cta_back()) pub_key_menu = Menu(self.ctx, pub_key_menu_items) while True: _, status = pub_key_menu.run_loop() diff --git a/src/krux/pages/home_pages/sign_message_ui.py b/src/krux/pages/home_pages/sign_message_ui.py index d3d2d5da..8c0af5cb 100644 --- a/src/krux/pages/home_pages/sign_message_ui.py +++ b/src/krux/pages/home_pages/sign_message_ui.py @@ -24,7 +24,7 @@ from embit import bip32, compact import hashlib import binascii -from .. import MENU_CONTINUE, LOAD_FROM_CAMERA, LOAD_FROM_SD, Menu +from .. import MENU_CONTINUE, LOAD_FROM_CAMERA, LOAD_FROM_SD, Menu, cta_back from ...themes import theme from ...display import ( DEFAULT_PADDING, @@ -199,7 +199,7 @@ def sign_message(self): t("Sign to SD card"), None if not self.has_sd_card() else lambda: None, ), - (t("Back"), lambda: None), + cta_back(lambda: None), ], ) index, _ = sign_menu.run_loop() diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 9c3c42f9..38a05a35 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -27,7 +27,7 @@ from ..display import DEFAULT_PADDING, FONT_HEIGHT, BOTTOM_PROMPT_LINE from ..krux_settings import Settings from ..qr import FORMAT_UR -from ..key import Key, P2WPKH +from ..key import Key, P2WSH, SCRIPT_LONG_NAMES from ..krux_settings import t from . import ( Page, @@ -37,6 +37,7 @@ ESC_KEY, LETTERS, choose_len_mnemonic, + cta_back, ) DIGITS = "0123456789" @@ -74,7 +75,7 @@ def load_key(self): (t("Via Camera"), self.load_key_from_camera), (t("Via Manual Input"), self.load_key_from_manual_input), (t("From Storage"), self.load_mnemonic_from_storage), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() @@ -92,7 +93,7 @@ def load_key_from_camera(self): "Tiny Seed", self.load_key_from_tiny_seed_image, ), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() @@ -109,7 +110,7 @@ def load_key_from_manual_input(self): (t("Word Numbers"), self.pre_load_key_from_digits), ("Tiny Seed (Bits)", self.load_key_from_tiny_seed), ("Stackbit 1248", self.load_key_from_1248), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() @@ -136,7 +137,7 @@ def new_key(self): (t("Via Words"), lambda: self.load_key_from_text(new=True)), (t("Via D6"), self.new_key_from_dice), (t("Via D20"), lambda: self.new_key_from_dice(True)), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() @@ -216,7 +217,10 @@ def _load_key_from_words(self, words, charset=LETTERS): multisig = Settings().wallet.multisig network = NETWORKS[Settings().wallet.network] account = 0 - script_type = P2WPKH + if multisig: + script_type = P2WSH + else: + script_type = SCRIPT_LONG_NAMES.get(Settings().wallet.script_type) from ..wallet import Wallet while True: @@ -241,7 +245,7 @@ def _load_key_from_words(self, words, charset=LETTERS): (t("Load Wallet"), lambda: None), (t("Passphrase"), lambda: None), (t("Customize"), lambda: None), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], offset=info_len * FONT_HEIGHT + DEFAULT_PADDING, ) @@ -548,7 +552,7 @@ def pre_load_key_from_digits(self): (t("Decimal"), self.load_key_from_digits), (t("Hexadecimal"), self.load_key_from_hexadecimal), (t("Octal"), self.load_key_from_octal), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ) index, status = submenu.run_loop() diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py index fefc3590..bb0e456f 100644 --- a/src/krux/pages/qr_view.py +++ b/src/krux/pages/qr_view.py @@ -22,7 +22,7 @@ import qrcode from embit.wordlists.bip39 import WORDLIST -from . import Page, Menu, MENU_CONTINUE, MENU_EXIT, ESC_KEY +from . import Page, Menu, MENU_CONTINUE, MENU_EXIT, ESC_KEY, cta_back from ..themes import theme, WHITE, BLACK from ..krux_settings import t from ..qr import get_size @@ -53,7 +53,7 @@ def __init__(self, ctx, binary=False, data=None, title=None): self.ctx = ctx self.binary = binary if data: - self.code = qrcode.encode(data) + self.code = qrcode.encode(data) # pylint: disable=E1101 self.title = title else: if self.binary: @@ -73,11 +73,11 @@ def _seed_qr(self): numbers = "" for word in words: numbers += str("%04d" % WORDLIST.index(word)) - return qrcode.encode(numbers) + return qrcode.encode(numbers) # pylint: disable=E1101 def _binary_seed_qr(self): binary_seed = self._to_compact_seed_qr(self.ctx.wallet.key.mnemonic) - return qrcode.encode(binary_seed) + return qrcode.encode(binary_seed) # pylint: disable=E1101 def _to_compact_seed_qr(self, mnemonic): mnemonic = mnemonic.split(" ") @@ -414,7 +414,7 @@ def save_qr_image_menu(self): ), ) ) - qr_menu.append((t("Back"), lambda: None)) + qr_menu.append(cta_back()) submenu = Menu(self.ctx, qr_menu, offset=2 * FONT_HEIGHT) submenu.run_loop() return MENU_CONTINUE @@ -487,7 +487,7 @@ def toggle_brightness(): ), ), (t("Print to QR"), printer_func), - (t("Back to Menu"), lambda: MENU_EXIT), + cta_back(label=t("Back to Menu")), ] submenu = Menu(self.ctx, qr_menu) _, status = submenu.run_loop() diff --git a/src/krux/pages/settings_page.py b/src/krux/pages/settings_page.py index e3f2e25d..6f726f4a 100644 --- a/src/krux/pages/settings_page.py +++ b/src/krux/pages/settings_page.py @@ -51,6 +51,7 @@ MENU_EXIT, ESC_KEY, DEFAULT_PADDING, + cta_back, ) import os @@ -233,9 +234,9 @@ def handler(): # Case for "Back" on the main Settings if settings_namespace.namespace == Settings.namespace: items.append((t("Factory Settings"), self.restore_settings)) - items.append((t("Back"), self._settings_exit_check)) + items.append(cta_back(self._settings_exit_check)) else: - items.append((t("Back"), lambda: MENU_EXIT)) + items.append(cta_back()) submenu = Menu(self.ctx, items) index, status = submenu.run_loop() diff --git a/src/krux/pages/tools.py b/src/krux/pages/tools.py index 69b3d417..82edbe3a 100644 --- a/src/krux/pages/tools.py +++ b/src/krux/pages/tools.py @@ -29,12 +29,12 @@ Page, Menu, MENU_CONTINUE, - MENU_EXIT, ESC_KEY, LETTERS, UPPERCASE_LETTERS, NUM_SPECIAL_1, NUM_SPECIAL_2, + cta_back, ) from .file_manager import SD_ROOT_PATH from ..format import generate_thousands_separator @@ -55,7 +55,7 @@ def __init__(self, ctx): (t("Descriptor Addresses"), self.descriptor_addresses), (t("Remove Mnemonic"), self.rm_stored_mnemonic), (t("Wipe Device"), self.wipe_device), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], ), ) diff --git a/src/krux/pages/wallet_settings.py b/src/krux/pages/wallet_settings.py index b4eb7e4b..2db11e78 100644 --- a/src/krux/pages/wallet_settings.py +++ b/src/krux/pages/wallet_settings.py @@ -34,6 +34,7 @@ UPPERCASE_LETTERS, NUM_SPECIAL_1, NUM_SPECIAL_2, + cta_back, ) from .settings_page import DIGITS from ..key import SINGLESIG_SCRIPT_PURPOSE, MULTISIG_SCRIPT_PURPOSE @@ -62,7 +63,7 @@ def load_passphrase_menu(self): [ (t("Type BIP39 Passphrase"), self._load_passphrase), (t("Scan BIP39 Passphrase"), self._load_qr_passphrase), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], disable_statusbar=True, ) @@ -134,7 +135,7 @@ def customize_wallet(self, key): ("Single/Multisig", lambda: None), (t("Script Type"), (lambda: None) if not multisig else None), (t("Account"), lambda: None), - (t("Back"), lambda: MENU_EXIT), + cta_back(), ], offset=info_len * FONT_HEIGHT + DEFAULT_PADDING, ) diff --git a/tests/pages/test_settings_page.py b/tests/pages/test_settings_page.py index 29a19c53..8cd410fb 100644 --- a/tests/pages/test_settings_page.py +++ b/tests/pages/test_settings_page.py @@ -36,7 +36,7 @@ def test_settings_m5stickv(m5stickv, mocker, mocker_printer): BUTTON_PAGE_PREV, # Go back to the second option - testnet BUTTON_ENTER, # Leave Default Wallet - BUTTON_PAGE, + *([BUTTON_PAGE] * 2), BUTTON_ENTER, # Leave Settings BUTTON_PAGE_PREV, @@ -219,7 +219,7 @@ def test_settings_on_amigo_tft(amigo, mocker, mocker_printer): NEXT_INDEX, GO_INDEX, # Back from wallet - 2, + 3, # Leave Settings LEAVE_INDEX, ), @@ -309,6 +309,7 @@ def test_change_display_type_on_amigo(amigo, mocker): BTN_SEQUENCE = [ *([BUTTON_PAGE] * 2), # Move to "Hardware" BUTTON_ENTER, # Enter "Hardware" + BUTTON_PAGE, # Change to "Display" BUTTON_ENTER, # Enter "Display" BUTTON_ENTER, # Enter "BGR colors" BUTTON_PAGE, # Change "BGR Type" @@ -329,7 +330,7 @@ def test_change_display_type_on_amigo(amigo, mocker): BUTTON_ENTER, # Confirm "Type" BUTTON_PAGE, # Move to "Back" BUTTON_ENTER, # Confirm "Back" from display - BUTTON_PAGE_PREV, # Move to "Back" + *([BUTTON_PAGE_PREV] * 2), # Move to "Back" BUTTON_ENTER, # Confirm "Back" from hardware *([BUTTON_PAGE_PREV] * 3), # Move to "Back" BUTTON_ENTER, # Confirm "Back" from settings @@ -436,26 +437,30 @@ def test_leave_settings_without_changes(amigo, mocker): BTN_SEQUENCES = [ [ # Change something then give up - BUTTON_ENTER, # Change "Bitcoin" + BUTTON_ENTER, # Change "Default Wallet" + BUTTON_PAGE, # Move to "Network" + BUTTON_ENTER, # Enter "Network" BUTTON_PAGE, # Change to testnet - BUTTON_ENTER, # Confirm testnet - BUTTON_ENTER, # Change "Bitcoin" again + BUTTON_ENTER, # Confirm "testnet" + BUTTON_ENTER, # Change "Network" again BUTTON_PAGE, # Change back to mainnet BUTTON_ENTER, # Confirm mainnet - BUTTON_PAGE_PREV, # Move to "Back" + *([BUTTON_PAGE] * 2), # Move to "Back" BUTTON_ENTER, # Confirm "Back" + BUTTON_PAGE_PREV, # Move to "Back" + BUTTON_ENTER, # Leave settings ], [ # Change persist then give up - [BUTTON_PAGE] * 3, # Move to "Persist" - BUTTON_ENTER, # Change "Persist" + *([BUTTON_PAGE] * 4), # Move to "Persist" + BUTTON_ENTER, # Enter "Persist" BUTTON_PAGE, # Change to SD BUTTON_ENTER, # Confirm SD BUTTON_ENTER, # Change "Persist" again BUTTON_PAGE, # Change back to flash BUTTON_ENTER, # Confirm flash - BUTTON_PAGE_PREV, # Move to "Back" - BUTTON_ENTER, # Confirm "Back" + *([BUTTON_PAGE] * 4), # Move to "Back" + BUTTON_ENTER, # Leave settings ], [ # Don't change anything @@ -479,6 +484,7 @@ def test_leave_settings_without_changes(amigo, mocker): persisted_to_flash_call = mocker.call( "Changes persisted to Flash!", duration=2500 ) + assert ctx.input.wait_for_button.call_count == len(btn_sequence) assert persisted_to_flash_call not in settings_page.flash_text.call_args_list @@ -488,12 +494,12 @@ def test_leave_settings_with_changes(amigo, mocker, mocker_sd_card_ok): from krux.input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV BTN_SEQUENCE = [ - BUTTON_ENTER, # Go to "Wallet" + BUTTON_ENTER, # Go to "Default Wallet" BUTTON_PAGE, # Go to "Network" BUTTON_ENTER, # Enter "Network" BUTTON_PAGE, # Change to testnet BUTTON_ENTER, # Confirm "testnet" - BUTTON_PAGE, # Move to back + *([BUTTON_PAGE] * 2), # Move to back BUTTON_ENTER, # Leave "Wallet" BUTTON_PAGE_PREV, # Move to "Back" BUTTON_ENTER, # Confirm "Back" @@ -504,6 +510,7 @@ def test_leave_settings_with_changes(amigo, mocker, mocker_sd_card_ok): # Leave settings without changes settings_page.settings() + assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE) settings_page.flash_text.assert_has_calls( [ mocker.call( diff --git a/tests/test_settings.py b/tests/test_settings.py index f9381ccf..48fab248 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -204,24 +204,32 @@ def test_all_labels(mocker, m5stickv): ThemeSettings, TouchSettings, ButtonsSettings, - AmgDisplaySettings, + DisplayAmgSettings, + DisplaySettings, + HardwareSettings, + SecuritySettings, + Settings, ) - bitcoin = DefaultWallet() + wallet = DefaultWallet() i18n = I18nSettings() - encryption = EncryptionSettings() - printer = PrinterSettings() thermal = ThermalSettings() adafruit = AdafruitPrinterSettings() cnc = CNCSettings() gbrl = GRBLSettings() + printer = PrinterSettings() + buttons = ButtonsSettings() + touch = TouchSettings() + amigo_display = DisplayAmgSettings() + display = DisplaySettings() + hardware = HardwareSettings() persist = PersistSettings() + encryption = EncryptionSettings() appearance = ThemeSettings() - touch = TouchSettings() - buttons = ButtonsSettings() - amigo_display = AmgDisplaySettings() + security = SecuritySettings() + settings = Settings() - assert bitcoin.label("network") + assert wallet.label("network") assert i18n.label("locale") assert encryption.label("version") assert printer.label("thermal") @@ -235,3 +243,7 @@ def test_all_labels(mocker, m5stickv): assert touch.label("threshold") assert buttons.label("debounce") assert amigo_display.label("flipped_x") + assert display.label("brightness") + assert hardware.label("printer") + assert security.label("auto_shutdown") + assert settings.label("persist")