from __future__ import annotations import typing as t from enum import Enum from trezorlib.debuglink import LayoutType from .. import buttons from .. import translations as TR if t.TYPE_CHECKING: from trezorlib.debuglink import DebugLink, LayoutContent AllActionsType = t.List[t.Union[str, t.Tuple[str, ...]]] # Passphrases and addresses for both models class CommonPass: RANDOM_25 = "Y@14lw%p)JN@f54MYvys@zj'g" RANDOM_25_ADDRESS = "mnkoxeaMzLgfCxUdDSZWrGactyJJerQVW6" SHORT = "abc123ABC_<>" SHORT_ADDRESS = "mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG" WITH_SPACE = "abc 123" WITH_SPACE_ADDRESS = "mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT" EMPTY_ADDRESS = "mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q" class PassphraseCategory(Enum): MENU = "MENU" DIGITS = "123" LOWERCASE = "abc" UPPERCASE = "ABC" SPECIAL = "#$!" def get_char_category(char: str) -> PassphraseCategory: """What is the category of a character""" if char.isdigit(): return PassphraseCategory.DIGITS if char.islower(): return PassphraseCategory.LOWERCASE if char.isupper(): return PassphraseCategory.UPPERCASE return PassphraseCategory.SPECIAL def go_next(debug: "DebugLink") -> LayoutContent: if debug.layout_type is LayoutType.TT: return debug.click(buttons.OK) elif debug.layout_type is LayoutType.TR: return debug.press_right() elif debug.layout_type is LayoutType.Mercury: return debug.swipe_up() else: raise RuntimeError("Unknown model") def tap_to_confirm(debug: "DebugLink") -> LayoutContent: if debug.layout_type is LayoutType.TT: return debug.read_layout() elif debug.layout_type is LayoutType.TR: return debug.read_layout() elif debug.layout_type is LayoutType.Mercury: return debug.click(buttons.TAP_TO_CONFIRM) else: raise RuntimeError("Unknown model") def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent: if debug.layout_type in (LayoutType.TT, LayoutType.Mercury): return debug.click(buttons.CANCEL) elif debug.layout_type is LayoutType.TR: if r_middle: return debug.press_middle() else: return debug.press_left() else: raise RuntimeError("Unknown model") def navigate_to_action_and_press( debug: "DebugLink", wanted_action: str, all_actions: AllActionsType, is_carousel: bool = True, hold_ms: int = 0, ) -> None: """Navigate to the button with certain action and press it""" # Orient try: _get_action_index(wanted_action, all_actions) except ValueError: raise ValueError(f"Action {wanted_action} is not supported in {all_actions}") # Navigate layout = debug.read_layout() current_action = layout.get_middle_choice() current_index = _get_action_index(current_action, all_actions) wanted_index = _get_action_index(wanted_action, all_actions) if not is_carousel: steps = wanted_index - current_index else: steps = _carousel_steps(current_index, wanted_index, len(all_actions)) if steps < 0: for _ in range(-steps): layout = debug.press_left() else: for _ in range(steps): layout = debug.press_right() # Press or hold debug.press_middle(hold_ms=hold_ms) def _carousel_steps(current_index: int, wanted_index: int, length: int) -> int: steps_forward = (wanted_index - current_index) % length steps_backward = (current_index - wanted_index) % length return steps_forward if steps_forward <= steps_backward else -steps_backward def unlock_gesture(debug: "DebugLink") -> LayoutContent: if debug.layout_type is LayoutType.TT: return debug.click(buttons.OK) elif debug.layout_type is LayoutType.TR: return debug.press_right() elif debug.layout_type is LayoutType.Mercury: return debug.click(buttons.TAP_TO_CONFIRM) else: raise RuntimeError("Unknown model") def _get_action_index(wanted_action: str, all_actions: AllActionsType) -> int: """Get index of the action in the list of all actions""" if wanted_action in all_actions: return all_actions.index(wanted_action) for index, action in enumerate(all_actions): if not isinstance(action, tuple): action = (action,) for subaction in action: try: tr = TR.translate(subaction) except KeyError: continue if tr == wanted_action: return index raise ValueError(f"Action {wanted_action} is not supported in {all_actions}")