diff --git a/core/assets/model_r/delete.png b/core/assets/model_r/delete.png index 2de55e4cde..f480bc2bc9 100644 Binary files a/core/assets/model_r/delete.png and b/core/assets/model_r/delete.png differ diff --git a/core/embed/rust/src/trezorhal/random.rs b/core/embed/rust/src/trezorhal/random.rs index f1a659c732..4f346564cd 100644 --- a/core/embed/rust/src/trezorhal/random.rs +++ b/core/embed/rust/src/trezorhal/random.rs @@ -10,11 +10,23 @@ pub fn shuffle(slice: &mut [T]) { } } +/// Returns a random number in the range [min, max]. pub fn uniform_between(min: u32, max: u32) -> u32 { assert!(max > min); uniform(max - min + 1) + min } +/// Returns a random number in the range [min, max] except one `except` number. +pub fn uniform_between_except(min: u32, max: u32, except: u32) -> u32 { + // Generate uniform_between as long as it is not except + loop { + let rand = uniform_between(min, max); + if rand != except { + return rand; + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -27,4 +39,11 @@ mod tests { assert!((256..=512).contains(&uniform_between(256, 512))); } } + + #[test] + fn uniform_between_except_test() { + for _ in 0..10 { + assert!(uniform_between_except(10, 12, 11) != 11); + } + } } diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index a7869ec197..8ad0e949bd 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -545,6 +545,15 @@ impl ButtonLayout { ) } + /// Left text and right arrow. + pub fn text_none_arrow(text: StrBuffer) -> Self { + Self::new( + Some(ButtonDetails::text(text)), + None, + Some(ButtonDetails::right_arrow_icon()), + ) + } + /// Only right text. pub fn none_none_text(text: StrBuffer) -> Self { Self::new(None, None, Some(ButtonDetails::text(text))) diff --git a/core/embed/rust/src/ui/model_tr/component/frame.rs b/core/embed/rust/src/ui/model_tr/component/frame.rs index 96cd54bde8..ed68f9c4cb 100644 --- a/core/embed/rust/src/ui/model_tr/component/frame.rs +++ b/core/embed/rust/src/ui/model_tr/component/frame.rs @@ -36,13 +36,10 @@ where } /// Aligning the title to the center, instead of the left. - /// Also disabling scrollbar in the positive case, as they are not - /// compatible. - pub fn with_title_center(mut self, title_centered: bool) -> Self { - self.title_centered = title_centered; - if title_centered { - self.account_for_scrollbar = false; - } + /// Also disabling scrollbar, as they are not compatible. + pub fn with_title_centered(mut self) -> Self { + self.title_centered = true; + self.account_for_scrollbar = false; self } diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/choice_item.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/choice_item.rs index 68981a7661..e1192639dd 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/choice_item.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/choice_item.rs @@ -44,6 +44,17 @@ impl ChoiceItem { self } + /// Getting the offset of the icon to center it vertically. + /// Depending on its size and used font. + fn icon_vertical_offset(&self) -> Offset { + if let Some(icon) = self.icon { + let height_diff = self.font.text_height() - icon.height(); + Offset::y(-height_diff / 2) + } else { + Offset::zero() + } + } + /// Getting the text width in pixels. pub fn text_width(&self) -> i16 { self.font.text_width(&self.text) @@ -88,7 +99,11 @@ impl ChoiceItem { /// Showing only the icon, if available, otherwise the text. pub fn render_left(&self, area: Rect) { if let Some(icon) = self.icon { - icon.draw_bottom_right(area.bottom_right(), theme::FG, theme::BG); + icon.draw_bottom_right( + area.bottom_right() + self.icon_vertical_offset(), + theme::FG, + theme::BG, + ); } else { display_right(area.bottom_right(), &self.text, self.font); } @@ -98,7 +113,11 @@ impl ChoiceItem { /// Showing only the icon, if available, otherwise the text. pub fn render_right(&self, area: Rect) { if let Some(icon) = self.icon { - icon.draw_bottom_left(area.bottom_left(), theme::FG, theme::BG); + icon.draw_bottom_left( + area.bottom_left() + self.icon_vertical_offset(), + theme::FG, + theme::BG, + ); } else { display(area.bottom_left(), &self.text, self.font); } @@ -135,7 +154,7 @@ impl Choice for ChoiceItem { if let Some(icon) = self.icon { let fg_color = if inverse { theme::BG } else { theme::FG }; let bg_color = if inverse { theme::FG } else { theme::BG }; - icon.draw_bottom_left(baseline, fg_color, bg_color); + icon.draw_bottom_left(baseline + self.icon_vertical_offset(), fg_color, bg_color); baseline = baseline + Offset::x(icon.width() + 2); } if inverse { diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs index 71ed4dc12d..35a9a4cbae 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs @@ -193,10 +193,12 @@ impl Component for PinEntry { _ => { if !self.is_full() { self.append_new_digit(ctx, page_counter); - // Choosing any random digit to be shown next - let new_page_counter = random::uniform_between( + // Choosing random digit to be shown next, but different + // from the current choice. + let new_page_counter = random::uniform_between_except( NUMBER_START_INDEX as u32, (CHOICE_LENGTH - 1) as u32, + page_counter as u32, ); self.choice_page .set_page_counter(ctx, new_page_counter as u8); diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs index fdcb9cff13..dc0dcdf6ee 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs @@ -75,6 +75,13 @@ impl SimpleChoice { self } + /// Show choices even when they do not fit entirely. + pub fn with_show_incomplete(mut self) -> Self { + self.choice_page = self.choice_page.with_incomplete(true); + self + } + + /// Returning chosen page index instead of the string result. pub fn with_return_index(mut self) -> Self { self.return_index = true; self diff --git a/core/embed/rust/src/ui/model_tr/component/loader.rs b/core/embed/rust/src/ui/model_tr/component/loader.rs index 60b6b4d478..1f5e0900a1 100644 --- a/core/embed/rust/src/ui/model_tr/component/loader.rs +++ b/core/embed/rust/src/ui/model_tr/component/loader.rs @@ -149,8 +149,6 @@ impl Loader { // NOTE: need to calculate this in `i32`, it would overflow using `i16` let invert_from = ((self.area.width() as i32 + 1) * done) / (display::LOADER_MAX as i32); - // TODO: the text should be moved one pixel to the top so it is centered in the - // loader display::bar_with_text_and_fill( self.area, Some(&self.text_overlay), @@ -167,7 +165,8 @@ impl Component for Loader { fn place(&mut self, bounds: Rect) -> Rect { self.area = bounds; - let baseline = bounds.bottom_center() + Offset::new(1, -1); + // Centering the text in the loader rectangle. + let baseline = bounds.bottom_center() + Offset::new(1, -2); self.text_overlay.place(baseline); self.area } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index e2e6735827..29ed6280f0 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -414,7 +414,7 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs: .text_bold("ADDRESS MISMATCH?".into()) .newline() .newline_half() - .text_mono("Please contact Trezor support on trezor.io/support".into()) + .text_mono("Please contact Trezor support at trezor.io/support".into()) } _ => unreachable!(), } @@ -565,7 +565,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj tutorial_screen( "HELLO".into(), "Welcome to Trezor.\nPress right to continue.".into(), - ButtonLayout::cancel_none_arrow(), + ButtonLayout::text_none_arrow("SKIP".into()), ButtonActions::last_none_next(), ) }, @@ -609,7 +609,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj Font::MONO, ) .newline() - .text_mono("Tutorial finished.".into()) + .text_mono("Tutorial complete.".into()) .newline() .newline() .alignment(Alignment::Center) @@ -655,10 +655,9 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M let max_count: u32 = kwargs.get(Qstr::MP_QSTR_max_count)?.try_into()?; let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; - let obj = LayoutObj::new(Frame::new( - title, - NumberInput::new(min_count, max_count, count), - ))?; + let obj = LayoutObj::new( + Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(), + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -736,10 +735,10 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) Frame::new( title, SimpleChoice::new(words, false) - .with_only_one_item() + .with_show_incomplete() .with_return_index(), ) - .with_title_center(true), + .with_title_centered(), )?; Ok(obj.into()) }; @@ -756,7 +755,9 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu .into_iter() .collect(); - let obj = LayoutObj::new(Frame::new(title, SimpleChoice::new(choices, false)))?; + let obj = LayoutObj::new( + Frame::new(title, SimpleChoice::new(choices, false)).with_title_centered(), + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -767,7 +768,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let obj = LayoutObj::new( - Frame::new(prompt, WordlistEntry::new(WordlistType::Bip39)).with_title_center(true), + Frame::new(prompt, WordlistEntry::new(WordlistType::Bip39)).with_title_centered(), )?; Ok(obj.into()) }; @@ -779,7 +780,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let obj = LayoutObj::new( - Frame::new(prompt, WordlistEntry::new(WordlistType::Slip39)).with_title_center(true), + Frame::new(prompt, WordlistEntry::new(WordlistType::Slip39)).with_title_centered(), )?; Ok(obj.into()) }; diff --git a/core/embed/rust/src/ui/model_tr/res/delete.toif b/core/embed/rust/src/ui/model_tr/res/delete.toif index 8b51bd5853..04cf849f95 100644 Binary files a/core/embed/rust/src/ui/model_tr/res/delete.toif and b/core/embed/rust/src/ui/model_tr/res/delete.toif differ diff --git a/core/embed/rust/src/ui/model_tr/theme.rs b/core/embed/rust/src/ui/model_tr/theme.rs index 2b7d857165..60311c20bd 100644 --- a/core/embed/rust/src/ui/model_tr/theme.rs +++ b/core/embed/rust/src/ui/model_tr/theme.rs @@ -48,7 +48,7 @@ pub const ICON_CANCEL: IconAndName = IconAndName::new( "cancel", ); // 8*8 pub const ICON_DELETE: IconAndName = - IconAndName::new(include_res!("model_tr/res/delete.toif"), "delete"); // 12*8 + IconAndName::new(include_res!("model_tr/res/delete.toif"), "delete"); // 10*7 pub const ICON_EYE: IconAndName = IconAndName::new(include_res!("model_tr/res/eye_round.toif"), "eye"); // 12*7 pub const ICON_LOCK: IconAndName = IconAndName::new(include_res!("model_tr/res/lock.toif"), "lock"); // 10*10 diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index b7d0e9c9fd..756ab695b8 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -9,6 +9,7 @@ from trezor.enums import ( ) from trezor.strings import format_amount from trezor.ui import layouts +from trezor.ui.layouts import confirm_metadata, confirm_properties from apps.common.paths import address_n_to_str @@ -21,9 +22,6 @@ from .helpers.utils import ( format_stake_pool_id, ) -confirm_metadata = layouts.confirm_metadata # global_import_cache -confirm_properties = layouts.confirm_properties # global_import_cache - if TYPE_CHECKING: from typing import Literal diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index f2ef2a1d3e..ea1c85abfe 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -121,7 +121,7 @@ async def error_pin_invalid(ctx: Context) -> NoReturn: await show_error_and_raise( ctx, "warning_wrong_pin", - "The PIN you entered is invalid.", + "PIN you have entered is not valid.", "Wrong PIN", # header exc=wire.PinInvalid, ) diff --git a/core/src/apps/management/change_pin.py b/core/src/apps/management/change_pin.py index 677ce8367f..69ff1e88d9 100644 --- a/core/src/apps/management/change_pin.py +++ b/core/src/apps/management/change_pin.py @@ -28,7 +28,11 @@ async def change_pin(ctx: Context, msg: ChangePin) -> Success: await _require_confirm_change_pin(ctx, msg) # get old pin - curpin, salt = await request_pin_and_sd_salt(ctx, "Enter old PIN") + if msg.remove: + prompt = "Enter PIN" + else: + prompt = "Enter old PIN" + curpin, salt = await request_pin_and_sd_salt(ctx, prompt) # if changing pin, pre-check the entered pin before getting new pin if curpin and not msg.remove: diff --git a/core/src/apps/management/change_wipe_code.py b/core/src/apps/management/change_wipe_code.py index fb957a02d2..d233c518be 100644 --- a/core/src/apps/management/change_wipe_code.py +++ b/core/src/apps/management/change_wipe_code.py @@ -90,7 +90,7 @@ def _require_confirm_action( title, "enable wipe code?", [ - "Wipe code will\nbe used to delete this device.", + "Wipe code can be used to erase all data from this device.", ], ) diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py index 50c67bc4a0..bcbdddf689 100644 --- a/core/src/apps/management/recovery_device/homescreen.py +++ b/core/src/apps/management/recovery_device/homescreen.py @@ -65,7 +65,9 @@ async def _continue_recovery_process(ctx: GenericContext) -> Success: if is_first_step: # If we are starting recovery, ask for word count first... # _request_word_count - await layout.homescreen_dialog(ctx, "Select", "Select number of words") + await layout.homescreen_dialog( + ctx, "Select", "First select the number of words in your recovery seed" + ) # ask for the number of words word_count = await layout.request_word_count(ctx, dry_run) # ...and only then show the starting screen with word count. @@ -158,7 +160,7 @@ async def _finish_recovery( storage_recovery.end_progress() await show_success( - ctx, "success_recovery", "You have successfully recovered your wallet." + ctx, "success_recovery", "You have finished recovering your wallet." ) return Success(message="Device recovered") @@ -196,7 +198,7 @@ async def _request_share_first_screen(ctx: GenericContext, word_count: int) -> N ) else: # BIP-39 await layout.homescreen_dialog( - ctx, "Enter seed", "Enter recovery seed", f"({word_count} words)" + ctx, "Enter seed", "Now enter your recovery seed", f"({word_count} words)" ) diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index 9d5366bed6..d254d86676 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -52,7 +52,7 @@ async def request_mnemonic( ctx, "request_word", "WORD ENTERING", - description="You'll only have to select first 2-3 letters.", + description="You'll only have to select the first 2-3 letters.", verb="CONTINUE", verb_cancel=None, br_code=ButtonRequestType.MnemonicInput, @@ -103,14 +103,7 @@ async def show_dry_run_result( from trezor.ui.layouts import show_success if result: - if is_slip39: - text = text_r( - "The entered recovery\nshares are valid and\nmatch what is currently\nin the device." - ) - else: - text = text_r( - "The entered recovery\nseed is valid and\nmatches the one\nin the device." - ) + text = "You have finished verifying your recovery seed" await show_success(ctx, "success_dry_recovery", text, button="Continue") else: if is_slip39: diff --git a/core/src/apps/management/reset_device/layout.py b/core/src/apps/management/reset_device/layout.py index 37df9d0f48..b210c922aa 100644 --- a/core/src/apps/management/reset_device/layout.py +++ b/core/src/apps/management/reset_device/layout.py @@ -12,8 +12,6 @@ from trezor.ui.layouts.reset import ( # noqa: F401 slip39_show_checklist, ) -from .. import text_r - if TYPE_CHECKING: from typing import Sequence from trezor.wire import GenericContext @@ -170,8 +168,9 @@ async def show_backup_warning(ctx: GenericContext, slip39: bool = False) -> None async def show_backup_success(ctx: GenericContext) -> None: - text = text_r("Use your backup\nwhen you need to\nrecover your wallet.") - await show_success(ctx, "success_backup", text, "Your backup is done.") + from trezor.ui.layouts.reset import show_success_backup + + await show_success_backup(ctx) # BIP39 diff --git a/core/src/apps/misc/cipher_key_value.py b/core/src/apps/misc/cipher_key_value.py index e65c8e9412..0aa93d34b5 100644 --- a/core/src/apps/misc/cipher_key_value.py +++ b/core/src/apps/misc/cipher_key_value.py @@ -24,11 +24,20 @@ async def cipher_key_value(ctx: Context, msg: CipherKeyValue) -> CipheredKeyValu encrypt = msg.encrypt decrypt = not msg.encrypt if (encrypt and msg.ask_on_encrypt) or (decrypt and msg.ask_on_decrypt): - if encrypt: - title = "Encrypt value" + # Special case for Trezor Suite, which asks for setting up labels + if msg.key == "Enable labeling?": + title = "SUITE LABELING" + verb = "ENABLE" else: - title = "Decrypt value" - await confirm_action(ctx, "cipher_key_value", title, description=msg.key) + if encrypt: + title = "Encrypt value" + else: + title = "Decrypt value" + verb = "CONFIRM" + + await confirm_action( + ctx, "cipher_key_value", title, description=msg.key, verb=verb + ) node = keychain.derive(msg.address_n) diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index aae0021700..2b4a445f00 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -510,6 +510,7 @@ async def confirm_action( hold: bool = False, hold_danger: bool = False, reverse: bool = False, + uppercase_title: bool = True, exc: ExceptionType = ActionCancelled, br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> None: @@ -535,7 +536,7 @@ async def confirm_action( ctx, RustLayout( trezorui2.confirm_action( - title=title.upper(), + title=title.upper() if uppercase_title else title, action=action, description=description, verb=verb, @@ -566,15 +567,12 @@ async def confirm_reset_device( if show_tutorial: await tutorial(ctx) - to_show = "By continuing you agree to our terms and conditions.\nMore info at trezor.io/tos." - if not recovery: - to_show += "\nUse you backup to recover your wallet." - return await _placeholder_confirm( ctx, "recover_device" if recovery else "setup_device", - "WALLET RECOVERY" if recovery else "WALLET BACKUP", - description=to_show, + "WALLET RECOVERY" if recovery else "WALLET CREATION", + description="By continuing you agree to Trezor Company terms and conditions.\n\rMore info at trezor.io/tos", + verb="RECOVER WALLET" if recovery else "CREATE WALLET", br_code=ButtonRequestType.ProtectCall if recovery else ButtonRequestType.ResetDevice, @@ -587,14 +585,14 @@ async def confirm_backup(ctx: GenericContext) -> bool: ctx, "backup_device", "SUCCESS", - "New wallet created successfully!\nYou should back up your new wallet right now.", + description="New wallet has been created.\nIt should be backed up now!", verb="BACK UP", verb_cancel="SKIP", br_code=ButtonRequestType.ResetDevice, ): return True - confirmed = await get_bool( + return await get_bool( ctx, "backup_device", "WARNING", @@ -604,7 +602,6 @@ async def confirm_backup(ctx: GenericContext) -> bool: verb_cancel="SKIP", br_code=ButtonRequestType.ResetDevice, ) - return confirmed async def confirm_path_warning( @@ -771,10 +768,25 @@ def show_success( subheader: str | None = None, button: str = "Continue", ) -> Awaitable[None]: + title = "Success" + + # In case only subheader is supplied, showing it + # in regular font, not bold. + if not content and subheader: + content = subheader + subheader = None + + # Special case for Shamir backup - to show everything just on one page + # in regular font. + if "Continue with" in content: + content = f"{subheader}\n{content}" + subheader = None + title = "" + return _show_modal( ctx, br_type, - "Success", + title, subheader, content, button_confirm=button, @@ -1192,7 +1204,7 @@ async def show_popup( def request_passphrase_on_host() -> None: draw_simple( trezorui2.show_info( - title="", + title="HIDDEN WALLET", description="Please type your passphrase on the connected host.", ) ) @@ -1226,7 +1238,9 @@ async def request_pin_on_device( ) -> str: from trezor import wire - if attempts_remaining is None: + # Not showing the prompt in case user did not enter it badly yet + # (has full 16 attempts left) + if attempts_remaining is None or attempts_remaining == 16: subprompt = "" elif attempts_remaining == 1: subprompt = "Last attempt" @@ -1260,7 +1274,7 @@ async def confirm_reenter_pin( ctx, br_type, "CHECK PIN", - "Please re-enter to confirm.", + description="Please re-enter PIN to confirm.", verb="BEGIN", br_code=br_code, ) @@ -1294,7 +1308,7 @@ async def confirm_pin_action( ctx, br_type, title, - f"{description} {action}", + description=f"{description} {action}", br_code=br_code, ) @@ -1317,21 +1331,22 @@ async def confirm_set_new_pin( br_code=br_code, ) - # TODO: this is a hack to put the next info on new screen in case of wipe code - # TODO: there should be a possibility to give a list of strings and each of them - # would be rendered on a new screen - if len(information) == 1: - information.append("\n") + if "wipe_code" in br_type: + title = "WIPE CODE INFO" + verb = "HODL TO BEGIN" # Easter egg from @Hannsek + else: + title = "PIN INFORMATION" + information.append( + "Position of individual numbers will change between entries for enhanced security." + ) + verb = "HOLD TO BEGIN" - information.append( - "Position of individual numbers will change between entries for more security." - ) return await confirm_action( ctx, br_type, - title="", + title=title, description="\n".join(information), - verb="HOLD TO BEGIN", + verb=verb, hold=True, br_code=br_code, ) diff --git a/core/src/trezor/ui/layouts/tr/recovery.py b/core/src/trezor/ui/layouts/tr/recovery.py index 68154c7a9a..1d46af6120 100644 --- a/core/src/trezor/ui/layouts/tr/recovery.py +++ b/core/src/trezor/ui/layouts/tr/recovery.py @@ -79,7 +79,7 @@ async def continue_recovery( if dry_run: title = "SEED CHECK" else: - title = "RECOVERY MODE" + title = "WALLET RECOVERY" return await get_bool( ctx, diff --git a/core/src/trezor/ui/layouts/tr/reset.py b/core/src/trezor/ui/layouts/tr/reset.py index 61c49aba08..c939b3bcad 100644 --- a/core/src/trezor/ui/layouts/tr/reset.py +++ b/core/src/trezor/ui/layouts/tr/reset.py @@ -47,18 +47,18 @@ async def show_share_words( ) if share_index is None: - check_title = "CHECK SEED" + check_title = "CHECK BACKUP" elif group_index is None: check_title = f"CHECK SHARE #{share_index + 1}" else: - check_title = f"CHECK G{group_index + 1} - SHARE {share_index + 1}" + check_title = f"GROUP {group_index + 1} - SHARE {share_index + 1}" if await get_bool( ctx, "backup_words", check_title, None, - "Select correct words in correct positions.", + "Select correct word for each position.", verb_cancel="SEE AGAIN", verb="BEGIN", br_code=ButtonRequestType.ResetDevice, @@ -84,7 +84,7 @@ async def select_word( RustLayout( trezorui2.select_word( # type: ignore [Argument missing for parameter "description"] title=f"SELECT {format_ordinal(checked_index + 1).upper()} WORD", - words=(words[0].upper(), words[1].upper(), words[2].upper()), + words=(words[0].lower(), words[1].lower(), words[2].lower()), ) ) ) @@ -165,10 +165,11 @@ async def slip39_prompt_threshold( await confirm_action( ctx, "slip39_prompt_threshold", - "Set threshold", + "Threshold", description="= number of shares needed for recovery", verb="BEGIN", verb_cancel=None, + uppercase_title=False, ) count = num_of_shares // 2 + 1 @@ -199,9 +200,10 @@ async def slip39_prompt_number_of_shares( ctx, "slip39_shares", "Number of shares", - description="= total number of unique word lists used for wallet backup.", + description="= total number of unique word lists used for wallet backup", verb="BEGIN", verb_cancel=None, + uppercase_title=False, ) count = 5 @@ -256,21 +258,26 @@ async def slip39_advanced_prompt_group_threshold( async def show_warning_backup(ctx: GenericContext, slip39: bool) -> None: - if slip39: - description = ( - "Never make a digital copy of your shares and never upload them online." - ) - else: - description = ( - "Never make a digital copy of your seed and never upload it online." - ) - await confirm_action( ctx, "backup_warning", - "Caution", - description=description, - verb="I understand", - verb_cancel=None, + "SHAMIR BACKUP" if slip39 else "WALLET BACKUP", + description="You can use your backup to recover your wallet at any time.", + verb="HOLD TO BEGIN", + hold=True, br_code=ButtonRequestType.ResetDevice, ) + + +async def show_success_backup(ctx: GenericContext) -> None: + from . import confirm_action + + await confirm_action( + ctx, + "success_backup", + "BACKUP IS DONE", + description="Keep it safe!", + verb="CONTINUE", + verb_cancel=None, + br_code=ButtonRequestType.Success, + ) diff --git a/core/src/trezor/ui/layouts/tt_v2/reset.py b/core/src/trezor/ui/layouts/tt_v2/reset.py index 86843033c3..d3bdb63b13 100644 --- a/core/src/trezor/ui/layouts/tt_v2/reset.py +++ b/core/src/trezor/ui/layouts/tt_v2/reset.py @@ -331,3 +331,10 @@ async def show_warning_backup(ctx: GenericContext, slip39: bool) -> None: ) if result != CONFIRMED: raise ActionCancelled + + +async def show_success_backup(ctx: GenericContext) -> None: + from . import show_success + + text = "Use your backup when you need to recover your wallet." + await show_success(ctx, "success_backup", text, "Your backup is done.") diff --git a/storage/storage.c b/storage/storage.c index 80e5040eb6..8f78064bfc 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -992,6 +992,7 @@ static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) { static void ensure_not_wipe_code(const uint8_t *pin, size_t pin_len) { if (sectrue != is_not_wipe_code(pin, pin_len)) { storage_wipe(); + // TODO: need to account for smaller model R - smaller font and different copy error_shutdown("You have entered the", "wipe code. All private", "data has been erased.", NULL); } diff --git a/tests/click_tests/recovery.py b/tests/click_tests/recovery.py index 9ebb4f0e8a..6e0b8cb199 100644 --- a/tests/click_tests/recovery.py +++ b/tests/click_tests/recovery.py @@ -35,7 +35,7 @@ def select_number_of_words( layout = debug.read_layout() # select number of words - assert "Select number of words" in layout.get_content() + assert "select the number of words" in layout.get_content() layout = debug.click(buttons.OK, wait=True) if legacy_ui: assert layout.text == "WordSelector" diff --git a/tests/click_tests/test_autolock.py b/tests/click_tests/test_autolock.py index eff4594218..012d89ddff 100644 --- a/tests/click_tests/test_autolock.py +++ b/tests/click_tests/test_autolock.py @@ -131,7 +131,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle layout = debug.click(buttons.OK, wait=True) assert layout.text == "< PinKeyboard >" layout = debug.input(PIN4, wait=True) - assert "Select number of words " in layout.get_content() + assert "select the number of words " in layout.get_content() # wait for autolock to trigger time.sleep(10.1) @@ -146,7 +146,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle layout = debug.input(PIN4, wait=True) # we are back at homescreen - assert "Select number of words" in layout.get_content() + assert "select the number of words" in layout.get_content() @pytest.mark.setup_client(pin=PIN4) diff --git a/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py b/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py index e4667b67b0..a57c6d2fad 100644 --- a/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py +++ b/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py @@ -58,7 +58,7 @@ def do_recover_core(client: Client, mnemonic: List[str], **kwargs: Any): client.debug.click(buttons.OK) yield - assert "Select number of words" in layout().get_content() + assert "select the number of words" in layout().get_content() client.debug.click(buttons.OK) yield @@ -70,7 +70,7 @@ def do_recover_core(client: Client, mnemonic: List[str], **kwargs: Any): client.debug.click(buttons.grid34(index % 3, index // 3)) yield - assert "Enter recovery seed" in layout().get_content() + assert "enter your recovery seed" in layout().get_content() client.debug.click(buttons.OK) yield @@ -97,7 +97,7 @@ def do_recover_r(client: Client, mnemonic: List[str], **kwargs: Any): client.debug.press_right() yield - assert "Select number of words" in layout().text + assert "select the number of words" in layout().text client.debug.press_yes() yield @@ -110,7 +110,7 @@ def do_recover_r(client: Client, mnemonic: List[str], **kwargs: Any): client.debug.input(str(len(mnemonic))) yield - assert "Enter recovery seed" in layout().text + assert "enter your recovery seed" in layout().text client.debug.press_yes() yield @@ -173,7 +173,7 @@ def test_invalid_seed_core(client: Client): client.debug.click(buttons.OK) yield - assert "Select number of words" in layout().get_content() + assert "select the number of words" in layout().get_content() client.debug.click(buttons.OK) yield @@ -182,7 +182,7 @@ def test_invalid_seed_core(client: Client): client.debug.click(buttons.grid34(0, 2)) yield - assert "Enter recovery seed" in layout().get_content() + assert "enter your recovery seed" in layout().get_content() client.debug.click(buttons.OK) yield @@ -197,7 +197,7 @@ def test_invalid_seed_core(client: Client): yield # retry screen - assert "Select number of words" in layout().get_content() + assert "select the number of words" in layout().get_content() client.debug.click(buttons.CANCEL) yield @@ -210,7 +210,7 @@ def test_invalid_seed_core(client: Client): client.debug.press_right() yield - assert "Select number of words" in layout().text + assert "select the number of words" in layout().text client.debug.press_yes() yield @@ -220,7 +220,7 @@ def test_invalid_seed_core(client: Client): client.debug.press_middle() yield - assert "Enter recovery seed" in layout().text + assert "enter your recovery seed" in layout().text client.debug.press_yes() yield @@ -239,7 +239,7 @@ def test_invalid_seed_core(client: Client): yield # retry screen - assert "Select number of words" in layout().text + assert "select the number of words" in layout().text client.debug.press_left() yield diff --git a/tests/device_tests/reset_recovery/test_recovery_bip39_t2.py b/tests/device_tests/reset_recovery/test_recovery_bip39_t2.py index fe37b129d6..00f8c63aa3 100644 --- a/tests/device_tests/reset_recovery/test_recovery_bip39_t2.py +++ b/tests/device_tests/reset_recovery/test_recovery_bip39_t2.py @@ -43,7 +43,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input("654") yield - assert "Select number of words" in layout().get_content() + assert "select the number of words" in layout().get_content() client.debug.press_yes() yield @@ -51,7 +51,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input(str(len(mnemonic))) yield - assert "Enter recovery seed" in layout().get_content() + assert "enter your recovery seed" in layout().get_content() client.debug.press_yes() yield @@ -75,7 +75,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input("654") yield - assert "re-enter to confirm" in layout().text + assert "re-enter PIN to confirm" in layout().text client.debug.press_right() yield @@ -83,7 +83,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input("654") yield - assert "Select number of words" in layout().text + assert "select the number of words" in layout().text client.debug.press_yes() yield @@ -92,7 +92,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input(str(len(mnemonic))) yield - assert "Enter recovery seed" in layout().text + assert "enter your recovery seed" in layout().text client.debug.press_yes() yield @@ -105,7 +105,7 @@ def test_tt_pin_passphrase(client: Client): client.debug.input(word) yield - assert "You have successfully recovered your wallet." in layout().text + assert "You have finished recovering your wallet." in layout().text client.debug.press_yes() with client: @@ -141,7 +141,7 @@ def test_tt_nopin_nopassphrase(client: Client): client.debug.press_yes() yield - assert "Select number of words" in layout().get_content() + assert "select the number of words" in layout().get_content() client.debug.press_yes() yield @@ -149,7 +149,7 @@ def test_tt_nopin_nopassphrase(client: Client): client.debug.input(str(len(mnemonic))) yield - assert "Enter recovery seed" in layout().get_content() + assert "enter your recovery seed" in layout().get_content() client.debug.press_yes() yield @@ -169,7 +169,7 @@ def test_tt_nopin_nopassphrase(client: Client): client.debug.press_yes() yield - assert "Select number of words" in layout().text + assert "select the number of words" in layout().text client.debug.press_yes() yield @@ -178,7 +178,7 @@ def test_tt_nopin_nopassphrase(client: Client): client.debug.input(str(len(mnemonic))) yield - assert "Enter recovery seed" in layout().text + assert "enter your recovery seed" in layout().text client.debug.press_yes() yield @@ -191,7 +191,7 @@ def test_tt_nopin_nopassphrase(client: Client): client.debug.input(word) yield - assert "You have successfully recovered your wallet." in layout().text + assert "You have finished recovering your wallet." in layout().text client.debug.press_yes() with client: diff --git a/tests/persistence_tests/test_shamir_persistence.py b/tests/persistence_tests/test_shamir_persistence.py index 01d80a0479..04688711ba 100644 --- a/tests/persistence_tests/test_shamir_persistence.py +++ b/tests/persistence_tests/test_shamir_persistence.py @@ -53,7 +53,7 @@ def test_abort(emulator: Emulator): assert layout.get_title() == "RECOVERY MODE" layout = debug.click(buttons.OK, wait=True) - assert "Select number of words" in layout.text + assert "select the number of words" in layout.text device_handler.restart(emulator) debug = device_handler.debuglink() @@ -63,7 +63,7 @@ def test_abort(emulator: Emulator): # no waiting for layout because layout doesn't change layout = debug.read_layout() - assert "Select number of words" in layout.text + assert "select the number of words" in layout.text layout = debug.click(buttons.CANCEL, wait=True) assert layout.get_title() == "ABORT RECOVERY"