diff --git a/core/Makefile b/core/Makefile index c961a98fe..5ecde87c3 100644 --- a/core/Makefile +++ b/core/Makefile @@ -24,6 +24,7 @@ BITCOIN_ONLY ?= 0 TREZOR_MODEL ?= T TREZOR_MEMPERF ?= 0 ADDRESS_SANITIZER ?= 0 +UI2 ?= 0 OPENOCD_INTERFACE ?= stlink # -or- ftdi/olimex-arm-usb-tiny-h OPENOCD_TRANSPORT ?= hla_swd # -or- jtag @@ -150,16 +151,16 @@ build_reflash: ## build reflash firmware + reflash image dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152 build_firmware: templates build_cross ## build firmware with frozen modules - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" $(FIRMWARE_BUILD_DIR)/firmware.bin + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" UI2="$(UI2)" $(FIRMWARE_BUILD_DIR)/firmware.bin build_unix: templates ## build unix port - $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" + $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" UI2="$(UI2)" build_unix_frozen: templates build_cross ## build unix port with frozen modules - $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 + $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" UI2="$(UI2)" TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 build_unix_debug: templates ## build unix port - $(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 TREZOR_EMULATOR_DEBUGGABLE=1 + $(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 UI2="$(UI2)" TREZOR_EMULATOR_DEBUGGABLE=1 build_cross: ## build mpy-cross port $(MAKE) -C vendor/micropython/mpy-cross $(CROSS_PORT_OPTS) diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 9fc109e74..30f683bdb 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -6,7 +6,7 @@ import os BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0') EVERYTHING = BITCOIN_ONLY != '1' TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') -NEW_UI = TREZOR_MODEL == '1' or os.environ.get('NEW_UI', '0') == '1' +UI2 = ARGUMENTS.get('UI2', '0') == '1' or TREZOR_MODEL == '1' FEATURE_FLAGS = { "RDI": True, @@ -181,9 +181,10 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', ] -if NEW_UI: +if UI2: CPPDEFINES_MOD += [ 'TREZOR_FONT_MEDIUM_ENABLE', + 'TREZOR_UI2', ] SOURCE_MOD += [ 'embed/extmod/modtrezorui/font_tthoves_bold_16.c', @@ -200,7 +201,7 @@ SOURCE_MOD += [ SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorproto.c', ] -if NEW_UI: +if UI2: SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorui2.c', ] @@ -555,7 +556,14 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py')) - if TREZOR_MODEL == 'T': + if TREZOR_MODEL == 'T' and UI2: + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/__init__.py')) + if EVERYTHING: + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/webauthn.py')) + elif TREZOR_MODEL == 'T': SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/__init__.py')) @@ -716,7 +724,7 @@ def cargo_build(): features = ["model_tt"] if BITCOIN_ONLY == "1": features.append("bitcoin_only") - if NEW_UI: + if UI2: features.append("ui") if PYOPT == "0": features.append("ui_debug") diff --git a/core/SConscript.unix b/core/SConscript.unix index eb7eb05cd..4ea096f18 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -6,7 +6,7 @@ import os BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0') EVERYTHING = BITCOIN_ONLY != '1' TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') -NEW_UI = TREZOR_MODEL == '1' or ARGUMENTS.get('NEW_UI', '0') == '1' +UI2 = ARGUMENTS.get('UI2', '0') == '1' or TREZOR_MODEL == '1' FEATURE_FLAGS = { "SECP256K1_ZKP": True, @@ -176,9 +176,10 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', ] -if NEW_UI: +if UI2: CPPDEFINES_MOD += [ 'TREZOR_FONT_MEDIUM_ENABLE', + 'TREZOR_UI2', ] SOURCE_MOD += [ 'embed/extmod/modtrezorui/font_tthoves_bold_16.c', @@ -197,7 +198,7 @@ SOURCE_MOD += [ SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorproto.c', ] -if NEW_UI: +if UI2: SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorui2.c', ] @@ -516,7 +517,14 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py')) - if TREZOR_MODEL == 'T': + if TREZOR_MODEL == 'T' and UI2: + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/__init__.py')) + if EVERYTHING: + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/webauthn.py')) + elif TREZOR_MODEL == 'T': SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt/__init__.py')) @@ -672,11 +680,10 @@ def cargo_build(): features = ["model_tt"] if BITCOIN_ONLY == "1": features.append("bitcoin_only") - if PYOPT == "0": - features.append("ui") - features.append("ui_debug") - elif NEW_UI: + if UI2: features.append("ui") + if PYOPT == "0": + features.append("ui_debug") return f'cd embed/rust; cargo build --profile {RUST_PROFILE} --target-dir=../../build/unix/rust --no-default-features --features "{" ".join(features)}"' diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index 059cd7c12..3abe5a1f2 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -31,20 +31,28 @@ #if TREZOR_MODEL == T // TT new rust UI -#if TREZOR_FONT_MEDIUM_ENABLE +#if TREZOR_UI2 +#ifdef TREZOR_FONT_NORMAL_ENABLE #include "font_tthoves_regular_18.h" #define FONT_NORMAL_DATA Font_TTHoves_Regular_18 #define FONT_NORMAL_HEIGHT 18 +#endif +#ifdef TREZOR_FONT_MEDIUM_ENABLE #include "font_tthoves_medium_20.h" #define FONT_MEDIUM_DATA Font_TTHoves_Medium_20 #define FONT_MEDIUM_HEIGHT 20 +#endif +#ifdef TREZOR_FONT_BOLD_ENABLE #include "font_tthoves_bold_16.h" #define FONT_BOLD_DATA Font_TTHoves_Bold_16 #define FONT_BOLD_HEIGHT 16 +#endif +#ifdef TREZOR_FONT_MONO_ENABLE #include "font_robotomono_regular_20.h" #define FONT_MONO_DATA Font_RobotoMono_Regular_20 #define FONT_MONO_HEIGHT 20 +#endif // TT old python UI #else diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils-meminfo.h b/core/embed/extmod/modtrezorutils/modtrezorutils-meminfo.h index 1c6ebd20c..56e2dbc7e 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils-meminfo.h +++ b/core/embed/extmod/modtrezorutils/modtrezorutils-meminfo.h @@ -654,9 +654,11 @@ void dump_value_opt(FILE *out, mp_const_obj_t value, bool eval_short) { dump_protodef(out, value); } +#ifdef TREZOR_UI2 else if (mp_obj_is_type(value, ui_debug_layout_type())) { dump_uilayout(out, value); } +#endif else { print_type(out, "unknown", NULL, value, true); diff --git a/core/embed/extmod/rustmods/modtrezorui2.c b/core/embed/extmod/rustmods/modtrezorui2.c index a347f474c..633209bb3 100644 --- a/core/embed/extmod/rustmods/modtrezorui2.c +++ b/core/embed/extmod/rustmods/modtrezorui2.c @@ -26,12 +26,12 @@ /// def layout_new_confirm_action( /// *, /// title: str, -/// action: str | None, -/// description: str | None, -/// verb: str | None, -/// verb_cancel: str | None, -/// hold: bool | None, -/// reverse: bool, +/// action: str | None = None, +/// description: str | None = None, +/// verb: str | None = None, +/// verb_cancel: str | None = None, +/// hold: bool | None = None, +/// reverse: bool = False, /// ) -> object: /// """Example layout.""" STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_action_obj, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index b2bffe509..e8b360c06 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -2,21 +2,21 @@ from typing import * # extmod/rustmods/modtrezorui2.c -def layout_new_example(text: str) -> None: +def layout_new_confirm_action( + *, + title: str, + action: str | None = None, + description: str | None = None, + verb: str | None = None, + verb_cancel: str | None = None, + hold: bool | None = None, + reverse: bool = False, +) -> object: """Example layout.""" # extmod/rustmods/modtrezorui2.c -def layout_new_confirm_action( - *, - title: str, - action: str | None, - description: str | None, - verb: str | None, - verb_cancel: str | None, - hold: bool | None, - reverse: bool, -) -> int: +def layout_new_example(text: str) -> object: """Example layout.""" @@ -26,5 +26,5 @@ def layout_new_confirm_text( title: str, data: str, description: str | None, -) -> int: +) -> object: """Example layout.""" diff --git a/core/src/all_modules.py b/core/src/all_modules.py index a7f4013f3..173baee16 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -204,6 +204,8 @@ trezor.ui.layouts.tt.recovery import trezor.ui.layouts.tt.recovery trezor.ui.layouts.tt.reset import trezor.ui.layouts.tt.reset +trezor.ui.layouts.tt_v2 +import trezor.ui.layouts.tt_v2 trezor.ui.loader import trezor.ui.loader trezor.ui.popup diff --git a/core/src/trezor/ui/__init__.py b/core/src/trezor/ui/__init__.py index 13745325d..51a7a30c1 100644 --- a/core/src/trezor/ui/__init__.py +++ b/core/src/trezor/ui/__init__.py @@ -444,60 +444,3 @@ class Layout(Component): def wait_until_layout_is_running() -> Awaitable[None]: # type: ignore while not layout_chan.takers: yield - - -class RustLayout(Layout): - # pylint: disable=super-init-not-called - def __init__(self, layout: Any): - self.layout = layout - self.timer = loop.Timer() - self.layout.set_timer_fn(self.set_timer) - - def set_timer(self, token: int, deadline: int) -> None: - self.timer.schedule(deadline, token) - - def create_tasks(self) -> tuple[loop.Task, ...]: - return self.handle_input_and_rendering(), self.handle_timers() - - if utils.MODEL == "T": - - def handle_input_and_rendering(self) -> loop.Task: # type: ignore - touch = loop.wait(io.TOUCH) - display.clear() - self.layout.paint() - while True: - # Using `yield` instead of `await` to avoid allocations. - event, x, y = yield touch - workflow.idle_timer.touch() - msg = None - if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END): - msg = self.layout.touch_event(event, x, y) - self.layout.paint() - if msg is not None: - raise Result(msg) - - elif utils.MODEL == "1": - - def handle_input_and_rendering(self) -> loop.Task: # type: ignore - button = loop.wait(io.BUTTON) - display.clear() - self.layout.paint() - while True: - # Using `yield` instead of `await` to avoid allocations. - event, button_num = yield button - workflow.idle_timer.touch() - msg = None - if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED): - msg = self.layout.button_event(event, button_num) - self.layout.paint() - if msg is not None: - raise Result(msg) - - def handle_timers(self) -> loop.Task: # type: ignore - while True: - # Using `yield` instead of `await` to avoid allocations. - token = yield self.timer - msg = self.layout.timer(token) - self.layout.paint() - if msg is not None: - raise Result(msg) diff --git a/core/src/trezor/ui/layouts/__init__.py b/core/src/trezor/ui/layouts/__init__.py index 45fcb49e4..f104a3dd2 100644 --- a/core/src/trezor/ui/layouts/__init__.py +++ b/core/src/trezor/ui/layouts/__init__.py @@ -2,11 +2,20 @@ from trezor import utils from .common import * # noqa: F401,F403 +try: + ui2 = True + import trezorui2 # noqa: F401 +except ImportError: + ui2 = False + # NOTE: using any import magic probably causes mypy not to check equivalence of # layout type signatures across models if utils.MODEL == "1": from .t1 import * # noqa: F401,F403 elif utils.MODEL == "T": - from .tt import * # noqa: F401,F403 + if not ui2: + from .tt import * # noqa: F401,F403 + else: + from .tt_v2 import * # noqa: F401,F403 else: raise ValueError("Unknown Trezor model") diff --git a/core/src/trezor/ui/layouts/t1.py b/core/src/trezor/ui/layouts/t1.py index db0fd9e48..0ae7d4fc4 100644 --- a/core/src/trezor/ui/layouts/t1.py +++ b/core/src/trezor/ui/layouts/t1.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from trezor import log, ui, wire +from trezor import io, log, loop, ui, wire, workflow from trezor.enums import ButtonRequestType from trezorui2 import layout_new_confirm_action, layout_new_confirm_text @@ -8,11 +8,49 @@ from trezorui2 import layout_new_confirm_action, layout_new_confirm_text from .common import interact if TYPE_CHECKING: - from typing import NoReturn, Type + from typing import Any, NoReturn, Type ExceptionType = BaseException | Type[BaseException] +class _RustLayout(ui.Layout): + # pylint: disable=super-init-not-called + def __init__(self, layout: Any): + self.layout = layout + self.timer = loop.Timer() + self.layout.set_timer_fn(self.set_timer) + + def set_timer(self, token: int, deadline: int) -> None: + self.timer.schedule(deadline, token) + + def create_tasks(self) -> tuple[loop.Task, ...]: + return self.handle_input_and_rendering(), self.handle_timers() + + def handle_input_and_rendering(self) -> loop.Task: # type: ignore + button = loop.wait(io.BUTTON) + ui.display.clear() + self.layout.paint() + while True: + # Using `yield` instead of `await` to avoid allocations. + event, button_num = yield button + workflow.idle_timer.touch() + msg = None + if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED): + msg = self.layout.button_event(event, button_num) + self.layout.paint() + if msg is not None: + raise ui.Result(msg) + + def handle_timers(self) -> loop.Task: # type: ignore + while True: + # Using `yield` instead of `await` to avoid allocations. + token = yield self.timer + msg = self.layout.timer(token) + self.layout.paint() + if msg is not None: + raise ui.Result(msg) + + async def confirm_action( ctx: wire.GenericContext, br_type: str, @@ -45,7 +83,7 @@ async def confirm_action( result = await interact( ctx, - ui.RustLayout( + _RustLayout( layout_new_confirm_action( title=title.upper(), action=action, @@ -75,7 +113,7 @@ async def confirm_text( ) -> None: result = await interact( ctx, - ui.RustLayout( + _RustLayout( layout_new_confirm_text( title=title.upper(), data=data, diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py new file mode 100644 index 000000000..05d7e7562 --- /dev/null +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -0,0 +1,452 @@ +from typing import TYPE_CHECKING + +from trezor import io, log, loop, ui, wire, workflow +from trezor.enums import ButtonRequestType + +from trezorui2 import layout_new_confirm_action + +from ...constants.tt import MONO_ADDR_PER_LINE +from ..common import interact + +if TYPE_CHECKING: + from typing import Any, Awaitable, Iterable, NoReturn, Sequence + + from ..common import PropertyType, ExceptionType + + +class _RustLayout(ui.Layout): + # pylint: disable=super-init-not-called + def __init__(self, layout: Any): + self.layout = layout + self.timer = loop.Timer() + self.layout.set_timer_fn(self.set_timer) + + def set_timer(self, token: int, deadline: int) -> None: + self.timer.schedule(deadline, token) + + def create_tasks(self) -> tuple[loop.Task, ...]: + return self.handle_input_and_rendering(), self.handle_timers() + + def handle_input_and_rendering(self) -> loop.Task: # type: ignore + touch = loop.wait(io.TOUCH) + ui.display.clear() + self.layout.paint() + # self.layout.bounds() + while True: + # Using `yield` instead of `await` to avoid allocations. + event, x, y = yield touch + workflow.idle_timer.touch() + msg = None + if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END): + msg = self.layout.touch_event(event, x, y) + self.layout.paint() + # self.layout.bounds() + if msg is not None: + raise ui.Result(msg) + + def handle_timers(self) -> loop.Task: # type: ignore + while True: + # Using `yield` instead of `await` to avoid allocations. + token = yield self.timer + msg = self.layout.timer(token) + self.layout.paint() + if msg is not None: + raise ui.Result(msg) + + +async def confirm_action( + ctx: wire.GenericContext, + br_type: str, + title: str, + action: str | None = None, + description: str | None = None, + description_param: str | None = None, + description_param_font: int = ui.BOLD, + verb: str | bytes | None = "CONFIRM", + verb_cancel: str | bytes | None = None, + hold: bool = False, + hold_danger: bool = False, + icon: str | None = None, + icon_color: int | None = None, + reverse: bool = False, + larger_vspace: bool = False, + exc: ExceptionType = wire.ActionCancelled, + br_code: ButtonRequestType = ButtonRequestType.Other, +) -> None: + if isinstance(verb, bytes) or isinstance(verb_cancel, bytes): + raise NotImplementedError + elif isinstance(verb, str): + verb = verb.upper() + + if description is not None and description_param is not None: + if description_param_font != ui.BOLD: + log.error(__name__, "confirm_action description_param_font not implemented") + description = description.format(description_param) + + if hold: + log.error(__name__, "confirm_action hold not implemented") + + if verb_cancel: + log.error(__name__, "confirm_action verb_cancel not implemented") + + result = await interact( + ctx, + _RustLayout( + layout_new_confirm_action( + title=title.upper(), + action=action, + description=description, + verb=verb, + hold=hold, + reverse=reverse, + ) + ), + br_type, + br_code, + ) + if result is not True: + raise exc + + +async def confirm_reset_device( + ctx: wire.GenericContext, prompt: str, recovery: bool = False +) -> None: + raise NotImplementedError + + +# TODO cleanup @ redesign +async def confirm_backup(ctx: wire.GenericContext) -> bool: + raise NotImplementedError + + +async def confirm_path_warning( + ctx: wire.GenericContext, path: str, path_type: str = "Path" +) -> None: + raise NotImplementedError + + +async def show_xpub( + ctx: wire.GenericContext, xpub: str, title: str, cancel: str +) -> None: + raise NotImplementedError + + +async def show_address( + ctx: wire.GenericContext, + address: str, + address_qr: str | None = None, + title: str = "Confirm address", + network: str | None = None, + multisig_index: int | None = None, + xpubs: Sequence[str] = (), + address_extra: str | None = None, + title_qr: str | None = None, +) -> None: + raise NotImplementedError + + +def show_pubkey( + ctx: wire.Context, pubkey: str, title: str = "Confirm public key" +) -> Awaitable[None]: + return confirm_blob( + ctx, + br_type="show_pubkey", + title="Confirm public key", + data=pubkey, + br_code=ButtonRequestType.PublicKey, + icon=ui.ICON_RECEIVE, + ) + + +async def _show_modal( + ctx: wire.GenericContext, + br_type: str, + br_code: ButtonRequestType, + header: str, + subheader: str | None, + content: str, + button_confirm: str | None, + button_cancel: str | None, + icon: str, + icon_color: int, + exc: ExceptionType = wire.ActionCancelled, +) -> None: + raise NotImplementedError + + +async def show_error_and_raise( + ctx: wire.GenericContext, + br_type: str, + content: str, + header: str = "Error", + subheader: str | None = None, + button: str = "Close", + red: bool = False, + exc: ExceptionType = wire.ActionCancelled, +) -> NoReturn: + await _show_modal( + ctx, + br_type=br_type, + br_code=ButtonRequestType.Other, + header=header, + subheader=subheader, + content=content, + button_confirm=None, + button_cancel=button, + icon=ui.ICON_WRONG, + icon_color=ui.RED if red else ui.ORANGE_ICON, + exc=exc, + ) + raise exc + + +def show_warning( + ctx: wire.GenericContext, + br_type: str, + content: str, + header: str = "Warning", + subheader: str | None = None, + button: str = "Try again", + br_code: ButtonRequestType = ButtonRequestType.Warning, + icon: str = ui.ICON_WRONG, + icon_color: int = ui.RED, +) -> Awaitable[None]: + return _show_modal( + ctx, + br_type=br_type, + br_code=br_code, + header=header, + subheader=subheader, + content=content, + button_confirm=button, + button_cancel=None, + icon=icon, + icon_color=icon_color, + ) + + +def show_success( + ctx: wire.GenericContext, + br_type: str, + content: str, + subheader: str | None = None, + button: str = "Continue", +) -> Awaitable[None]: + return _show_modal( + ctx, + br_type=br_type, + br_code=ButtonRequestType.Success, + header="Success", + subheader=subheader, + content=content, + button_confirm=button, + button_cancel=None, + icon=ui.ICON_CONFIRM, + icon_color=ui.GREEN, + ) + + +async def confirm_output( + ctx: wire.GenericContext, + address: str, + amount: str, + font_amount: int = ui.NORMAL, # TODO cleanup @ redesign + title: str = "Confirm sending", + subtitle: str | None = None, # TODO cleanup @ redesign + color_to: int = ui.FG, # TODO cleanup @ redesign + to_str: str = " to\n", # TODO cleanup @ redesign + to_paginated: bool = False, # TODO cleanup @ redesign + width: int = MONO_ADDR_PER_LINE, + width_paginated: int = MONO_ADDR_PER_LINE - 1, + br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + icon: str = ui.ICON_SEND, +) -> None: + raise NotImplementedError + + +async def should_show_more( + ctx: wire.GenericContext, + title: str, + para: Iterable[tuple[int, str]], + button_text: str = "Show all", + br_type: str = "should_show_more", + br_code: ButtonRequestType = ButtonRequestType.Other, + icon: str = ui.ICON_DEFAULT, + icon_color: int = ui.ORANGE_ICON, +) -> bool: + raise NotImplementedError + + +async def confirm_blob( + ctx: wire.GenericContext, + br_type: str, + title: str, + data: bytes | str, + description: str | None = None, + hold: bool = False, + br_code: ButtonRequestType = ButtonRequestType.Other, + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign + ask_pagination: bool = False, +) -> None: + raise NotImplementedError + + +def confirm_address( + ctx: wire.GenericContext, + title: str, + address: str, + description: str | None = "Address:", + br_type: str = "confirm_address", + br_code: ButtonRequestType = ButtonRequestType.Other, + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign +) -> Awaitable[None]: + raise NotImplementedError + + +async def confirm_text( + ctx: wire.GenericContext, + br_type: str, + title: str, + data: str, + description: str | None = None, + br_code: ButtonRequestType = ButtonRequestType.Other, + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign +) -> None: + raise NotImplementedError + + +def confirm_amount( + ctx: wire.GenericContext, + title: str, + amount: str, + description: str = "Amount:", + br_type: str = "confirm_amount", + br_code: ButtonRequestType = ButtonRequestType.Other, + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign +) -> Awaitable[None]: + raise NotImplementedError + + +async def confirm_properties( + ctx: wire.GenericContext, + br_type: str, + title: str, + props: Iterable[PropertyType], + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign + hold: bool = False, + br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, +) -> None: + raise NotImplementedError + + +async def confirm_total( + ctx: wire.GenericContext, + total_amount: str, + fee_amount: str, + title: str = "Confirm transaction", + total_label: str = "Total amount:\n", + fee_label: str = "\nincluding fee:\n", + icon_color: int = ui.GREEN, + br_type: str = "confirm_total", + br_code: ButtonRequestType = ButtonRequestType.SignTx, +) -> None: + raise NotImplementedError + + +async def confirm_joint_total( + ctx: wire.GenericContext, spending_amount: str, total_amount: str +) -> None: + raise NotImplementedError + + +async def confirm_metadata( + ctx: wire.GenericContext, + br_type: str, + title: str, + content: str, + param: str | None = None, + br_code: ButtonRequestType = ButtonRequestType.SignTx, + hide_continue: bool = False, + hold: bool = False, + param_font: int = ui.BOLD, + icon: str = ui.ICON_SEND, # TODO cleanup @ redesign + icon_color: int = ui.GREEN, # TODO cleanup @ redesign + larger_vspace: bool = False, # TODO cleanup @ redesign +) -> None: + raise NotImplementedError + + +async def confirm_replacement( + ctx: wire.GenericContext, description: str, txid: str +) -> None: + raise NotImplementedError + + +async def confirm_modify_output( + ctx: wire.GenericContext, + address: str, + sign: int, + amount_change: str, + amount_new: str, +) -> None: + raise NotImplementedError + + +async def confirm_modify_fee( + ctx: wire.GenericContext, + sign: int, + user_fee_change: str, + total_fee_new: str, +) -> None: + raise NotImplementedError + + +async def confirm_coinjoin( + ctx: wire.GenericContext, fee_per_anonymity: str | None, total_fee: str +) -> None: + raise NotImplementedError + + +# TODO cleanup @ redesign +async def confirm_sign_identity( + ctx: wire.GenericContext, proto: str, identity: str, challenge_visual: str | None +) -> None: + raise NotImplementedError + + +async def confirm_signverify( + ctx: wire.GenericContext, coin: str, message: str, address: str, verify: bool +) -> None: + raise NotImplementedError + + +async def show_popup( + title: str, + description: str, + subtitle: str | None = None, + description_param: str = "", + timeout_ms: int = 3000, +) -> None: + raise NotImplementedError + + +def draw_simple_text(title: str, description: str = "") -> None: + raise NotImplementedError + + +async def request_passphrase_on_device(ctx: wire.GenericContext, max_len: int) -> str: + raise NotImplementedError + + +async def request_pin_on_device( + ctx: wire.GenericContext, + prompt: str, + attempts_remaining: int | None, + allow_cancel: bool, +) -> str: + raise NotImplementedError