From d089fd3187bd1e73bc4c62dc6191c8badf2fac73 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Tue, 9 Aug 2022 18:26:37 +0200 Subject: [PATCH] feat(core): Implement SetBusy message. --- core/.changelog.d/2445.added | 1 + core/src/all_modules.py | 2 ++ core/src/apps/base.py | 46 +++++++++++++++++++++++++- core/src/apps/homescreen/busyscreen.py | 29 ++++++++++++++++ core/src/storage/cache.py | 3 ++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 core/.changelog.d/2445.added create mode 100644 core/src/apps/homescreen/busyscreen.py diff --git a/core/.changelog.d/2445.added b/core/.changelog.d/2445.added new file mode 100644 index 000000000..0c1c1eea4 --- /dev/null +++ b/core/.changelog.d/2445.added @@ -0,0 +1 @@ +Support SetBusy message. diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 05ec259c2..febd064a9 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -351,6 +351,8 @@ apps.debug.load_device import apps.debug.load_device apps.homescreen import apps.homescreen +apps.homescreen.busyscreen +import apps.homescreen.busyscreen apps.homescreen.homescreen import apps.homescreen.homescreen apps.homescreen.lockscreen diff --git a/core/src/apps/base.py b/core/src/apps/base.py index a9954a0f3..c9f5e4cac 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -20,9 +20,26 @@ if TYPE_CHECKING: Ping, DoPreauthorized, CancelAuthorization, + SetBusy, ) +def busy_expiry_ms() -> int: + """ + Returns the time left until the busy state expires or 0 if the device is not in the busy state. + """ + + busy_deadline_bytes = storage.cache.get(storage.cache.APP_COMMON_BUSY_DEADLINE_MS) + if busy_deadline_bytes is None: + return 0 + + import utime + + busy_deadline_ms = int.from_bytes(busy_deadline_bytes, "big") + expiry_ms = utime.ticks_diff(busy_deadline_ms, utime.ticks_ms()) + return expiry_ms if expiry_ms > 0 else 0 + + def get_features() -> Features: import storage.recovery import storage.sd_salt @@ -47,6 +64,7 @@ def get_features() -> Features: label=storage.device.get_label(), pin_protection=config.has_pin(), unlocked=config.is_unlocked(), + busy=busy_expiry_ms() > 0, ) if utils.BITCOIN_ONLY: @@ -145,6 +163,24 @@ async def handle_LockDevice(ctx: wire.Context, msg: LockDevice) -> Success: return Success() +async def handle_SetBusy(ctx: wire.Context, msg: SetBusy) -> Success: + if not storage.device.is_initialized(): + raise wire.NotInitialized("Device is not initialized") + + if msg.expiry_ms: + import utime + + deadline = utime.ticks_add(utime.ticks_ms(), msg.expiry_ms) + storage.cache.set( + storage.cache.APP_COMMON_BUSY_DEADLINE_MS, deadline.to_bytes(4, "big") + ) + else: + storage.cache.delete(storage.cache.APP_COMMON_BUSY_DEADLINE_MS) + set_homescreen() + workflow.close_others() + return Success() + + async def handle_EndSession(ctx: wire.Context, msg: EndSession) -> Success: storage.cache.end_current_session() return Success() @@ -200,13 +236,20 @@ ALLOW_WHILE_LOCKED = ( MessageType.LockDevice, MessageType.DoPreauthorized, MessageType.WipeDevice, + MessageType.SetBusy, ) def set_homescreen() -> None: import storage.recovery + import storage # workaround for https://github.com/microsoft/pyright/issues/2685 - if not config.is_unlocked(): + if storage.cache.is_set(storage.cache.APP_COMMON_BUSY_DEADLINE_MS): + from apps.homescreen.busyscreen import busyscreen + + workflow.set_default(busyscreen) + + elif not config.is_unlocked(): from apps.homescreen.lockscreen import lockscreen workflow.set_default(lockscreen) @@ -296,6 +339,7 @@ def boot() -> None: workflow_handlers.register( MessageType.CancelAuthorization, handle_CancelAuthorization ) + workflow_handlers.register(MessageType.SetBusy, handle_SetBusy) reload_settings_from_storage() if config.is_unlocked(): diff --git a/core/src/apps/homescreen/busyscreen.py b/core/src/apps/homescreen/busyscreen.py new file mode 100644 index 000000000..fffde579f --- /dev/null +++ b/core/src/apps/homescreen/busyscreen.py @@ -0,0 +1,29 @@ +import storage.cache +from trezor import loop, ui +from trezor.ui.layouts import draw_simple_text + +from apps.base import busy_expiry_ms, set_homescreen + +from . import HomescreenBase + + +async def busyscreen() -> None: + await Busyscreen() + + +class Busyscreen(HomescreenBase): + RENDER_INDICATOR = storage.cache.BUSYSCREEN_ON + + def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: + return self.handle_rendering(), self.handle_input(), self.handle_expiry() + + def handle_expiry(self) -> loop.Task: # type: ignore [awaitable-is-generator] + yield loop.sleep(busy_expiry_ms()) + storage.cache.delete(storage.cache.APP_COMMON_BUSY_DEADLINE_MS) + set_homescreen() + raise ui.Result(None) + + def do_render(self) -> None: + draw_simple_text( + "Please wait", "CoinJoin in progress.\n\nDo not disconnect your\nTrezor." + ) diff --git a/core/src/storage/cache.py b/core/src/storage/cache.py index d4544aced..a12e802fe 100644 --- a/core/src/storage/cache.py +++ b/core/src/storage/cache.py @@ -30,6 +30,7 @@ APP_COMMON_SEED_WITHOUT_PASSPHRASE = 0 | _SESSIONLESS_FLAG APP_COMMON_SAFETY_CHECKS_TEMPORARY = 1 | _SESSIONLESS_FLAG STORAGE_DEVICE_EXPERIMENTAL_FEATURES = 2 | _SESSIONLESS_FLAG APP_COMMON_REQUEST_PIN_LAST_UNLOCK = 3 | _SESSIONLESS_FLAG +APP_COMMON_BUSY_DEADLINE_MS = 4 | _SESSIONLESS_FLAG # === Homescreen storage === @@ -40,6 +41,7 @@ APP_COMMON_REQUEST_PIN_LAST_UNLOCK = 3 | _SESSIONLESS_FLAG # is still on. This way we can avoid unnecessary fadeins/fadeouts when a workflow ends. HOMESCREEN_ON = object() LOCKSCREEN_ON = object() +BUSYSCREEN_ON = object() homescreen_shown: object | None = None @@ -132,6 +134,7 @@ class SessionlessCache(DataCache): 1, # APP_COMMON_SAFETY_CHECKS_TEMPORARY 1, # STORAGE_DEVICE_EXPERIMENTAL_FEATURES 4, # APP_COMMON_REQUEST_PIN_LAST_UNLOCK + 4, # APP_COMMON_BUSY_DEADLINE_MS ) super().__init__()