From 75cee540a4576acd8347b575f9e27cb68315b418 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Sun, 30 Oct 2022 18:01:06 +0100 Subject: [PATCH] fix(core/monero): fix blinking progress indicators [no changelog] --- core/src/apps/monero/key_image_sync.py | 14 +- core/src/apps/monero/layout.py | 126 ++++++------------ core/src/apps/monero/live_refresh.py | 13 +- core/src/apps/monero/sign_tx.py | 29 ++-- .../signing/step_01_init_transaction.py | 8 +- .../apps/monero/signing/step_02_set_input.py | 10 +- .../apps/monero/signing/step_04_input_vini.py | 7 +- .../monero/signing/step_05_all_inputs_set.py | 8 +- .../apps/monero/signing/step_06_set_output.py | 12 +- .../monero/signing/step_07_all_outputs_set.py | 10 +- .../apps/monero/signing/step_09_sign_input.py | 7 +- core/src/trezor/ui/layouts/tt_v2/__init__.py | 12 ++ 12 files changed, 124 insertions(+), 132 deletions(-) diff --git a/core/src/apps/monero/key_image_sync.py b/core/src/apps/monero/key_image_sync.py index d282f01b8..80d39b5d9 100644 --- a/core/src/apps/monero/key_image_sync.py +++ b/core/src/apps/monero/key_image_sync.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: MoneroKeyImageSyncStepAck, MoneroKeyImageSyncStepRequest, ) + from trezor.ui.layouts.common import ProgressLayout from trezor.wire import Context from apps.common.keychain import Keychain @@ -34,9 +35,10 @@ async def key_image_sync( state = KeyImageSync() res = await _init_step(state, ctx, msg, keychain) + progress = layout.monero_keyimage_sync_progress() while state.current_output + 1 < state.num_outputs: step = await ctx.call(res, MoneroKeyImageSyncStepRequest) - res = await _sync_step(state, ctx, step) + res = _sync_step(state, ctx, step, progress) gc.collect() await ctx.call(res, MoneroKeyImageSyncFinalRequest) @@ -92,8 +94,11 @@ async def _init_step( return MoneroKeyImageExportInitAck() -async def _sync_step( - s: KeyImageSync, ctx: Context, tds: MoneroKeyImageSyncStepRequest +def _sync_step( + s: KeyImageSync, + ctx: Context, + tds: MoneroKeyImageSyncStepRequest, + progress: ProgressLayout, ) -> MoneroKeyImageSyncStepAck: from trezor import log from trezor.messages import ( @@ -111,7 +116,8 @@ async def _sync_step( buff = bytearray(32 * 3) buff_mv = memoryview(buff) - await layout.keyimage_sync_step(ctx, s.current_output, s.num_outputs) + if s.current_output is not None and s.num_outputs > 0: + progress.report(1000 * (s.current_output + 1) // s.num_outputs) for td in tds.tdis: s.current_output += 1 diff --git a/core/src/apps/monero/layout.py b/core/src/apps/monero/layout.py index 80c6fd72d..00d1107ea 100644 --- a/core/src/apps/monero/layout.py +++ b/core/src/apps/monero/layout.py @@ -1,9 +1,13 @@ from typing import TYPE_CHECKING -from trezor import ui from trezor.enums import ButtonRequestType -from trezor.ui.layouts import confirm_action, confirm_metadata -from trezor.ui.popup import Popup +from trezor.ui.layouts import ( # noqa: F401 + confirm_action, + confirm_metadata, + monero_keyimage_sync_progress, + monero_live_refresh_progress, + monero_transaction_progress_inner, +) DUMMY_PAYMENT_ID = b"\x00\x00\x00\x00\x00\x00\x00\x00" @@ -22,6 +26,35 @@ if TYPE_CHECKING: BRT_SignTx = ButtonRequestType.SignTx # global_import_cache +class MoneroTransactionProgress: + def __init__(self) -> None: + self.inner = None + + def step(self, state: State, step: int, sub_step: int = 0) -> None: + if step == 0 or self.inner is None: + self.inner = monero_transaction_progress_inner() + info = "Signing..." + elif step == state.STEP_INP: + info = f"Processing inputs\n{sub_step + 1}/{state.input_count}" + elif step == state.STEP_VINI: + info = f"Hashing inputs\n{sub_step + 1}/{state.input_count}" + elif step == state.STEP_ALL_IN: + info = "Processing..." + elif step == state.STEP_OUT: + info = f"Processing outputs\n{sub_step + 1}/{state.output_count}" + elif step == state.STEP_ALL_OUT: + info = "Postprocessing..." + elif step == state.STEP_SIGN: + info = f"Signing inputs\n{sub_step + 1}/{state.input_count}" + else: + info = "Processing..." + + state.progress_cur += 1 + + p = 1000 * state.progress_cur // state.progress_total + self.inner.report(p, info) + + def _format_amount(value: int) -> str: from trezor import strings @@ -78,6 +111,7 @@ async def require_confirm_transaction( state: State, tsx_data: MoneroTransactionData, network_type: MoneroNetworkType, + progress: MoneroTransactionProgress, ) -> None: """ Ask for confirmation from user. @@ -112,7 +146,7 @@ async def require_confirm_transaction( await _require_confirm_payment_id(ctx, payment_id) await _require_confirm_fee(ctx, tsx_data.fee) - await transaction_step(state, 0) + progress.step(state, 0) async def _require_confirm_output( @@ -173,87 +207,3 @@ async def _require_confirm_unlock_time(ctx: Context, unlock_time: int) -> None: str(unlock_time), BRT_SignTx, ) - - -class TransactionStep(ui.Component): - def __init__(self, state: State, info: list[str]) -> None: - super().__init__() - self.state = state - self.info = info - - def on_render(self) -> None: - from trezor import ui # local_cache_global - - state = self.state - info = self.info - ui.header("Signing transaction", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE) - p = 1000 * state.progress_cur // state.progress_total - ui.display.loader(p, False, -4, ui.WHITE, ui.BG) - ui.display.text_center(ui.WIDTH // 2, 210, info[0], ui.NORMAL, ui.FG, ui.BG) - if len(info) > 1: - ui.display.text_center(ui.WIDTH // 2, 235, info[1], ui.NORMAL, ui.FG, ui.BG) - - -class KeyImageSyncStep(ui.Component): - def __init__(self, current: int, total_num: int) -> None: - super().__init__() - self.current = current - self.total_num = total_num - - def on_render(self) -> None: - current = self.current - total_num = self.total_num - ui.header("Syncing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE) - p = (1000 * (current + 1) // total_num) if total_num > 0 else 0 - ui.display.loader(p, False, 18, ui.WHITE, ui.BG) - - -class LiveRefreshStep(ui.Component): - def __init__(self, current: int) -> None: - super().__init__() - self.current = current - - def on_render(self) -> None: - from trezor import ui # local_cache_global - - current = self.current - ui.header("Refreshing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE) - p = (1000 * current // 8) % 1000 - ui.display.loader(p, True, 18, ui.WHITE, ui.BG) - ui.display.text_center( - ui.WIDTH // 2, 145, str(current), ui.NORMAL, ui.FG, ui.BG - ) - - -async def transaction_step(state: State, step: int, sub_step: int = 0) -> None: - if step == 0: - info = ["Signing..."] - elif step == state.STEP_INP: - info = ["Processing inputs", f"{sub_step + 1}/{state.input_count}"] - elif step == state.STEP_VINI: - info = ["Hashing inputs", f"{sub_step + 1}/{state.input_count}"] - elif step == state.STEP_ALL_IN: - info = ["Processing..."] - elif step == state.STEP_OUT: - info = ["Processing outputs", f"{sub_step + 1}/{state.output_count}"] - elif step == state.STEP_ALL_OUT: - info = ["Postprocessing..."] - elif step == state.STEP_SIGN: - info = ["Signing inputs", f"{sub_step + 1}/{state.input_count}"] - else: - info = ["Processing..."] - - state.progress_cur += 1 - await Popup(TransactionStep(state, info)) - - -async def keyimage_sync_step(ctx: Context, current: int | None, total_num: int) -> None: - if current is None: - return - await Popup(KeyImageSyncStep(current, total_num)) - - -async def live_refresh_step(ctx: Context, current: int | None) -> None: - if current is None: - return - await Popup(LiveRefreshStep(current)) diff --git a/core/src/apps/monero/live_refresh.py b/core/src/apps/monero/live_refresh.py index 081941d4e..fc53eb220 100644 --- a/core/src/apps/monero/live_refresh.py +++ b/core/src/apps/monero/live_refresh.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: MoneroLiveRefreshFinalAck, MoneroLiveRefreshStartAck, ) + from trezor.ui.layouts.common import ProgressLayout from trezor.wire import Context from apps.common.keychain import Keychain @@ -28,6 +29,7 @@ async def live_refresh( state = LiveRefreshState() res = await _init_step(state, ctx, msg, keychain) + progress = layout.monero_live_refresh_progress() while True: step = await ctx.call_any( res, @@ -36,7 +38,7 @@ async def live_refresh( ) del res if MoneroLiveRefreshStepRequest.is_type_of(step): - res = await _refresh_step(state, ctx, step) + res = _refresh_step(state, ctx, step, progress) else: return MoneroLiveRefreshFinalAck() gc.collect() @@ -69,8 +71,11 @@ async def _init_step( return MoneroLiveRefreshStartAck() -async def _refresh_step( - s: LiveRefreshState, ctx: Context, msg: MoneroLiveRefreshStepRequest +def _refresh_step( + s: LiveRefreshState, + ctx: Context, + msg: MoneroLiveRefreshStepRequest, + progress: ProgressLayout, ) -> MoneroLiveRefreshStepAck: from trezor.messages import MoneroLiveRefreshStepAck from trezor import log @@ -81,7 +86,7 @@ async def _refresh_step( buff = bytearray(32 * 3) buff_mv = memoryview(buff) - await layout.live_refresh_step(ctx, s.current_output) + progress.report((1000 * s.current_output // 8) % 1000, str(s.current_output)) s.current_output += 1 if __debug__: diff --git a/core/src/apps/monero/sign_tx.py b/core/src/apps/monero/sign_tx.py index 3288c9f37..7c1618ae5 100644 --- a/core/src/apps/monero/sign_tx.py +++ b/core/src/apps/monero/sign_tx.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from apps.common.keychain import auto_keychain +from apps.monero.layout import MoneroTransactionProgress if TYPE_CHECKING: from trezor.messages import MoneroTransactionFinalAck @@ -19,6 +20,7 @@ async def sign_tx( state = State(ctx) mods = utils.unimport_begin() + progress = MoneroTransactionProgress() # Splitting ctx.call() to write() and read() helps to reduce memory fragmentation # between calls. @@ -28,7 +30,9 @@ async def sign_tx( gc.collect() gc.threshold(gc.mem_free() // 4 + gc.mem_alloc()) - result_msg, accept_msgs = await _sign_tx_dispatch(state, received_msg, keychain) + result_msg, accept_msgs = await _sign_tx_dispatch( + state, received_msg, keychain, progress + ) if accept_msgs is None: break @@ -42,7 +46,9 @@ async def sign_tx( return result_msg -async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: +async def _sign_tx_dispatch( + state: State, msg, keychain: Keychain, progress: MoneroTransactionProgress +) -> tuple: from trezor.enums import MessageType from trezor import wire @@ -53,7 +59,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: return ( await step_01_init_transaction.init_transaction( - state, msg.address_n, msg.network_type, msg.tsx_data, keychain + state, msg.address_n, msg.network_type, msg.tsx_data, keychain, progress ), (MessageType.MoneroTransactionSetInputRequest,), ) @@ -62,7 +68,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: from apps.monero.signing import step_02_set_input return ( - await step_02_set_input.set_input(state, msg.src_entr), + step_02_set_input.set_input(state, msg.src_entr, progress), ( MessageType.MoneroTransactionSetInputRequest, MessageType.MoneroTransactionInputViniRequest, @@ -73,8 +79,8 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: from apps.monero.signing import step_04_input_vini return ( - await step_04_input_vini.input_vini( - state, msg.src_entr, msg.vini, msg.vini_hmac, msg.orig_idx + step_04_input_vini.input_vini( + state, msg.src_entr, msg.vini, msg.vini_hmac, msg.orig_idx, progress ), ( MessageType.MoneroTransactionInputViniRequest, @@ -86,7 +92,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: from apps.monero.signing import step_05_all_inputs_set return ( - await step_05_all_inputs_set.all_inputs_set(state), + step_05_all_inputs_set.all_inputs_set(state, progress), (MessageType.MoneroTransactionSetOutputRequest,), ) @@ -98,8 +104,8 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: del msg return ( - await step_06_set_output.set_output( - state, dst, dst_hmac, rsig_data, is_offloaded_bp + step_06_set_output.set_output( + state, dst, dst_hmac, rsig_data, is_offloaded_bp, progress ), ( MessageType.MoneroTransactionSetOutputRequest, @@ -111,7 +117,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: from apps.monero.signing import step_07_all_outputs_set return ( - await step_07_all_outputs_set.all_outputs_set(state), + step_07_all_outputs_set.all_outputs_set(state, progress), (MessageType.MoneroTransactionSignInputRequest,), ) @@ -119,7 +125,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: from apps.monero.signing import step_09_sign_input return ( - await step_09_sign_input.sign_input( + step_09_sign_input.sign_input( state, msg.src_entr, msg.vini, @@ -129,6 +135,7 @@ async def _sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple: msg.pseudo_out_alpha, msg.spend_key, msg.orig_idx, + progress, ), ( MessageType.MoneroTransactionSignInputRequest, diff --git a/core/src/apps/monero/signing/step_01_init_transaction.py b/core/src/apps/monero/signing/step_01_init_transaction.py index 140274fe0..a81863816 100644 --- a/core/src/apps/monero/signing/step_01_init_transaction.py +++ b/core/src/apps/monero/signing/step_01_init_transaction.py @@ -17,6 +17,7 @@ if TYPE_CHECKING: MoneroTransactionRsigData, ) + from apps.monero.layout import MoneroTransactionProgress from apps.monero.signing.state import State @@ -26,6 +27,7 @@ async def init_transaction( network_type: int, tsx_data: MoneroTransactionData, keychain, + progress: MoneroTransactionProgress, ) -> MoneroTransactionInitAck: import gc from apps.monero.signing import offloading_keys @@ -55,7 +57,11 @@ async def init_transaction( # Ask for confirmation await layout.require_confirm_transaction( - state.ctx, state, tsx_data, state.creds.network_type + state.ctx, + state, + tsx_data, + state.creds.network_type, + progress, ) state.creds.address = None state.creds.network_type = None diff --git a/core/src/apps/monero/signing/step_02_set_input.py b/core/src/apps/monero/signing/step_02_set_input.py index 5437eb307..55c01bce6 100644 --- a/core/src/apps/monero/signing/step_02_set_input.py +++ b/core/src/apps/monero/signing/step_02_set_input.py @@ -21,24 +21,26 @@ if TYPE_CHECKING: MoneroTransactionSourceEntry, MoneroTransactionSetInputAck, ) + from apps.monero.layout import MoneroTransactionProgress from apps.monero.xmr import crypto -async def set_input( - state: State, src_entr: MoneroTransactionSourceEntry +def set_input( + state: State, + src_entr: MoneroTransactionSourceEntry, + progress: MoneroTransactionProgress, ) -> MoneroTransactionSetInputAck: from trezor.messages import MoneroTransactionSetInputAck from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey from apps.monero.xmr import chacha_poly, monero, serialize from apps.monero.signing import offloading_keys - from apps.monero import layout state.current_input_index += 1 current_input_index = state.current_input_index # local_cache_attribute amount = src_entr.amount # local_cache_attribute outputs = src_entr.outputs # local_cache_attribute - await layout.transaction_step(state, state.STEP_INP, current_input_index) + progress.step(state, state.STEP_INP, current_input_index) if state.last_step > state.STEP_INP: raise ValueError("Invalid state transition") diff --git a/core/src/apps/monero/signing/step_04_input_vini.py b/core/src/apps/monero/signing/step_04_input_vini.py index d07a73dba..995f2b1df 100644 --- a/core/src/apps/monero/signing/step_04_input_vini.py +++ b/core/src/apps/monero/signing/step_04_input_vini.py @@ -11,16 +11,17 @@ if TYPE_CHECKING: MoneroTransactionSourceEntry, ) from .state import State + from apps.monero.layout import MoneroTransactionProgress -async def input_vini( +def input_vini( state: State, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, vini_hmac: bytes, orig_idx: int, + progress: MoneroTransactionProgress, ) -> MoneroTransactionInputViniAck: - from apps.monero import layout from apps.monero.signing import offloading_keys from apps.monero.xmr import crypto @@ -28,7 +29,7 @@ async def input_vini( STEP_VINI = state.STEP_VINI # local_cache_attribute - await layout.transaction_step(state, STEP_VINI, state.current_input_index + 1) + progress.step(state, STEP_VINI, state.current_input_index + 1) if state.last_step not in (state.STEP_INP, STEP_VINI): raise ValueError("Invalid state transition") if state.current_input_index >= state.input_count: diff --git a/core/src/apps/monero/signing/step_05_all_inputs_set.py b/core/src/apps/monero/signing/step_05_all_inputs_set.py index 0d1bd6061..ca2890e99 100644 --- a/core/src/apps/monero/signing/step_05_all_inputs_set.py +++ b/core/src/apps/monero/signing/step_05_all_inputs_set.py @@ -8,16 +8,18 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from trezor.messages import MoneroTransactionAllInputsSetAck from .state import State + from apps.monero.layout import MoneroTransactionProgress -async def all_inputs_set(state: State) -> MoneroTransactionAllInputsSetAck: - from apps.monero import layout +def all_inputs_set( + state: State, progress: MoneroTransactionProgress +) -> MoneroTransactionAllInputsSetAck: from apps.monero.xmr import crypto from trezor.messages import MoneroTransactionAllInputsSetAck state.mem_trace(0) - await layout.transaction_step(state, state.STEP_ALL_IN) + progress.step(state, state.STEP_ALL_IN) if state.last_step != state.STEP_VINI: raise ValueError("Invalid state transition") diff --git a/core/src/apps/monero/signing/step_06_set_output.py b/core/src/apps/monero/signing/step_06_set_output.py index 4d6533a4e..b3661315c 100644 --- a/core/src/apps/monero/signing/step_06_set_output.py +++ b/core/src/apps/monero/signing/step_06_set_output.py @@ -12,6 +12,7 @@ from apps.monero.xmr import crypto, crypto_helpers if TYPE_CHECKING: from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import BulletproofPlus + from apps.monero.layout import MoneroTransactionProgress from trezor.messages import ( MoneroTransactionDestinationEntry, MoneroTransactionSetOutputAck, @@ -20,15 +21,14 @@ if TYPE_CHECKING: from .state import State -async def set_output( +def set_output( state: State, dst_entr: MoneroTransactionDestinationEntry, dst_entr_hmac: bytes, rsig_data: MoneroTransactionRsigData, - is_offloaded_bp=False, + is_offloaded_bp: bool, + progress: MoneroTransactionProgress, ) -> MoneroTransactionSetOutputAck: - from apps.monero import layout - mem_trace = state.mem_trace # local_cache_attribute mem_trace(0, True) @@ -36,9 +36,7 @@ async def set_output( # Progress update only for master message (skip for offloaded BP msg) if not is_offloaded_bp: - await layout.transaction_step( - state, state.STEP_OUT, state.current_output_index + 1 - ) + progress.step(state, state.STEP_OUT, state.current_output_index + 1) mem_trace(1, True) diff --git a/core/src/apps/monero/signing/step_07_all_outputs_set.py b/core/src/apps/monero/signing/step_07_all_outputs_set.py index 90438a325..650f01f4d 100644 --- a/core/src/apps/monero/signing/step_07_all_outputs_set.py +++ b/core/src/apps/monero/signing/step_07_all_outputs_set.py @@ -8,19 +8,21 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from trezor.messages import MoneroTransactionAllOutSetAck + from apps.monero.layout import MoneroTransactionProgress from .state import State -async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck: +def all_outputs_set( + state: State, progress: MoneroTransactionProgress +) -> MoneroTransactionAllOutSetAck: import gc - from apps.monero import layout mem_trace = state.mem_trace # local_cache_attribute mem_trace(0) - await layout.transaction_step(state, state.STEP_ALL_OUT) - mem_trace(1) + progress.step(state, state.STEP_ALL_OUT) + state.mem_trace(1) _validate(state) state.is_processing_offloaded = False diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py index 41a470e07..eba761986 100644 --- a/core/src/apps/monero/signing/step_09_sign_input.py +++ b/core/src/apps/monero/signing/step_09_sign_input.py @@ -14,12 +14,13 @@ import gc from typing import TYPE_CHECKING if TYPE_CHECKING: + from apps.monero.layout import MoneroTransactionProgress from trezor.messages import MoneroTransactionSourceEntry from trezor.messages import MoneroTransactionSignInputAck from .state import State -async def sign_input( +def sign_input( state: State, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, @@ -29,6 +30,7 @@ async def sign_input( pseudo_out_alpha_enc: bytes, spend_enc: bytes, orig_idx: int, + progress: MoneroTransactionProgress, ) -> MoneroTransactionSignInputAck: """ :param state: transaction state @@ -44,7 +46,6 @@ async def sign_input( :return: Generated signature MGs[i] """ from trezor import utils - from apps.monero import layout from apps.monero.xmr import crypto_helpers from apps.monero.xmr import crypto @@ -53,7 +54,7 @@ async def sign_input( input_count = state.input_count # local_cache_attribute outputs = src_entr.outputs # local_cache_attribute - await layout.transaction_step(state, state.STEP_SIGN, state.current_input_index + 1) + progress.step(state, state.STEP_SIGN, state.current_input_index + 1) state.current_input_index += 1 if state.last_step not in (state.STEP_ALL_OUT, state.STEP_SIGN): diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index e442d65d6..71fd2c552 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -1144,3 +1144,15 @@ def bitcoin_progress(message: str) -> ProgressLayout: def pin_progress(message: str, description: str) -> ProgressLayout: return RustProgress(message.upper(), description=description) + + +def monero_keyimage_sync_progress() -> ProgressLayout: + return RustProgress("SYNCING") + + +def monero_live_refresh_progress() -> ProgressLayout: + return RustProgress("REFRESHING", description="", indeterminate=True) + + +def monero_transaction_progress_inner() -> ProgressLayout: + return RustProgress("SIGNING TRANSACTION", description="")