1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-24 02:59:03 +00:00

feat(core): new design of recovery dialogs

[no changelog]
This commit is contained in:
grdddj 2023-06-29 17:26:51 +02:00 committed by Jiří Musil
parent 03f77c50e9
commit bc502287fc
22 changed files with 387 additions and 676 deletions

View File

@ -146,6 +146,7 @@ static void _librust_qstrs(void) {
MP_QSTR_value;
MP_QSTR_verb;
MP_QSTR_verb_cancel;
MP_QSTR_warning;
MP_QSTR_words;
MP_QSTR_wrong_pin;
MP_QSTR_xpubs;

View File

@ -24,8 +24,7 @@ use crate::{
},
ComponentExt, FormattedText, LineBreaking, Timeout,
},
display,
geometry::{self, Alignment},
display, geometry,
layout::{
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED, INFO},
@ -805,33 +804,6 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::none_armed_none(button.clone());
let btn_actions = ButtonActions::none_confirm_none();
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
.alignment(Alignment::Center)
.text_bold(title.clone())
.newline()
.text_normal(description.clone());
let formatted = FormattedText::new(ops).vertically_aligned(Alignment::Center);
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, 1);
let obj = LayoutObj::new(Flow::new(pages))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?;
@ -914,7 +886,7 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
};
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
let formatted = FormattedText::new(ops).vertically_aligned(Alignment::Center);
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
};
@ -990,6 +962,35 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let warning: StrBuffer = kwargs.get(Qstr::MP_QSTR_warning)?.try_into()?;
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::none_armed_none(button.clone());
let btn_actions = ButtonActions::none_confirm_none();
let mut ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL);
ops = ops.alignment(geometry::Alignment::Center);
if !warning.is_empty() {
ops = ops.text_bold(warning.clone()).newline().newline();
}
if !description.is_empty() {
ops = ops.text_normal(description.clone());
}
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, 1);
let obj = LayoutObj::new(Flow::new(pages))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
@ -1267,16 +1268,32 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let show_info: bool = kwargs.get(Qstr::MP_QSTR_show_info)?.try_into()?;
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]);
let mut paragraphs = ParagraphVecShort::new();
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, description));
if show_info {
let first = "You'll only have to select the first 2-3 letters of each word.";
let second =
"Position of the cursor will change between entries for enhanced security.";
paragraphs
.add(Paragraph::new(&theme::TEXT_NORMAL, first.into()))
.add(Paragraph::new(&theme::TEXT_NORMAL, second.into()));
}
let title = if dry_run {
"SEED CHECK"
"BACKUP CHECK"
} else {
"WALLET RECOVERY"
"RECOVER WALLET"
};
content_in_button_page(title.into(), paragraphs, button, Some("".into()), false)
content_in_button_page(
title.into(),
paragraphs.into_paragraphs(),
button,
Some("".into()),
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
@ -1561,15 +1578,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Show user how to interact with the device."""
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),
/// def show_error(
/// *,
/// title: str,
/// description: str,
/// button: str,
/// ) -> object:
/// """Show a popup with text centered both vertically and horizontally. With just a middle button."""
Qstr::MP_QSTR_show_error => obj_fn_kw!(0, new_show_error).as_obj(),
/// def confirm_modify_fee(
/// *,
/// title: str, # ignored
@ -1603,6 +1611,15 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Show multiple texts, each on its own page."""
Qstr::MP_QSTR_multiple_pages_texts => obj_fn_kw!(0, new_multiple_pages_texts).as_obj(),
/// def show_warning(
/// *,
/// button: str,
/// warning: str,
/// description: str,
/// ) -> object:
/// """Warning modal with middle button and centered text."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
/// def show_info(
/// *,
/// title: str,
@ -1717,6 +1734,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// button: str,
/// dry_run: bool,
/// info_button: bool, # unused on TR
/// show_info: bool,
/// ) -> object:
/// """Device recovery homescreen."""
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),

View File

@ -1399,7 +1399,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
.with_spacing(theme::RECOVERY_SPACING);
let notification = if dry_run {
"SEED CHECK"
"BACKUP CHECK"
} else {
"RECOVERY MODE"
};
@ -1427,9 +1427,9 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
let block = move |_args: &[Obj], kwargs: &Map| {
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let title = if dry_run {
"SEED CHECK"
"BACKUP CHECK"
} else {
"WALLET RECOVERY"
"RECOVER WALLET"
};
let paragraphs = Paragraphs::new(

View File

@ -1,19 +1,17 @@
from typing import *
CONFIRMED: object
CANCELLED: object
INFO: object
# rust/src/ui/model_tr/layout.rs
def disable_animation(disable: bool) -> None:
"""Disable animations, debug builds only."""
# rust/src/ui/model_tr/layout.rs
def toif_info(data: bytes) -> tuple[int, int, bool]:
"""Get TOIF image dimensions and format (width: int, height: int, is_grayscale: bool)."""
# rust/src/ui/model_tr/layout.rs
def confirm_action(
*,
@ -28,7 +26,6 @@ def confirm_action(
) -> object:
"""Confirm action."""
# rust/src/ui/model_tr/layout.rs
def confirm_blob(
*,
@ -42,7 +39,6 @@ def confirm_blob(
) -> object:
"""Confirm byte sequence data."""
# rust/src/ui/model_tr/layout.rs
def confirm_address(
*,
@ -53,7 +49,6 @@ def confirm_address(
) -> object:
"""Confirm address."""
# rust/src/ui/model_tr/layout.rs
def confirm_properties(
*,
@ -65,7 +60,6 @@ def confirm_properties(
the value is to be rendered as binary with monospace font, False otherwise.
This only concerns the text style, you need to decode the value to UTF-8 in python."""
# rust/src/ui/model_tr/layout.rs
def confirm_reset_device(
*,
@ -74,12 +68,10 @@ def confirm_reset_device(
) -> object:
"""Confirm TOS before device setup."""
# rust/src/ui/model_tr/layout.rs
def confirm_backup() -> object:
"""Strongly recommend user to do backup."""
# rust/src/ui/model_tr/layout.rs
def show_address_details(
*,
@ -91,7 +83,6 @@ def show_address_details(
) -> object:
"""Show address details - QR code, account, path, cosigner xpubs."""
# rust/src/ui/model_tr/layout.rs
def confirm_value(
*,
@ -103,7 +94,6 @@ def confirm_value(
) -> object:
"""Confirm value."""
# rust/src/ui/model_tr/layout.rs
def confirm_joint_total(
*,
@ -112,7 +102,6 @@ def confirm_joint_total(
) -> object:
"""Confirm total if there are external inputs."""
# rust/src/ui/model_tr/layout.rs
def confirm_modify_output(
*,
@ -123,7 +112,6 @@ def confirm_modify_output(
) -> object:
"""Decrease or increase amount for given address."""
# rust/src/ui/model_tr/layout.rs
def confirm_output(
*,
@ -135,7 +123,6 @@ def confirm_output(
) -> object:
"""Confirm output."""
# rust/src/ui/model_tr/layout.rs
def confirm_total(
*,
@ -148,12 +135,10 @@ def confirm_total(
) -> object:
"""Confirm summary of a transaction."""
# rust/src/ui/model_tr/layout.rs
def tutorial() -> object:
"""Show user how to interact with the device."""
# rust/src/ui/model_tr/layout.rs
def show_error(
*,
@ -163,7 +148,6 @@ def show_error(
) -> object:
"""Show a popup with text centered both vertically and horizontally. With just a middle button."""
# rust/src/ui/model_tr/layout.rs
def confirm_modify_fee(
*,
@ -175,7 +159,6 @@ def confirm_modify_fee(
) -> object:
"""Decrease or increase transaction fee."""
# rust/src/ui/model_tr/layout.rs
def confirm_fido(
*,
@ -188,7 +171,6 @@ def confirm_fido(
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_tr/layout.rs
def multiple_pages_texts(
*,
@ -198,6 +180,13 @@ def multiple_pages_texts(
) -> object:
"""Show multiple texts, each on its own page."""
def show_warning(
*,
button: str,
warning: str,
description: str,
) -> object:
"""Warning modal with middle button and centered text."""
# rust/src/ui/model_tr/layout.rs
def show_info(
@ -208,17 +197,14 @@ def show_info(
) -> object:
"""Info modal."""
# rust/src/ui/model_tr/layout.rs
def show_passphrase() -> object:
"""Show passphrase on host dialog."""
# rust/src/ui/model_tr/layout.rs
def show_mismatch() -> object:
"""Warning modal, receiving address mismatch."""
# rust/src/ui/model_tr/layout.rs
def confirm_with_info(
*,
@ -230,7 +216,6 @@ def confirm_with_info(
"""Confirm given items but with third button. Always single page
without scrolling."""
# rust/src/ui/model_tr/layout.rs
def confirm_coinjoin(
*,
@ -239,7 +224,6 @@ def confirm_coinjoin(
) -> object:
"""Confirm coinjoin authorization."""
# rust/src/ui/model_tr/layout.rs
def request_pin(
*,
@ -250,7 +234,6 @@ def request_pin(
) -> str | object:
"""Request pin on device."""
# rust/src/ui/model_tr/layout.rs
def request_passphrase(
*,
@ -259,7 +242,6 @@ def request_passphrase(
) -> str | object:
"""Get passphrase."""
# rust/src/ui/model_tr/layout.rs
def request_bip39(
*,
@ -267,14 +249,12 @@ def request_bip39(
) -> str:
"""Get recovery word for BIP39."""
# rust/src/ui/model_tr/layout.rs
def request_slip39(
*,
prompt: str,
) -> str:
"""SLIP39 word input keyboard."""
"""SLIP39 word input keyboard."""
# rust/src/ui/model_tr/layout.rs
def select_word(
@ -283,9 +263,8 @@ def select_word(
description: str,
words: Iterable[str],
) -> int:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_tr/layout.rs
def show_share_words(
@ -294,7 +273,6 @@ def show_share_words(
) -> object:
"""Shows a backup seed."""
# rust/src/ui/model_tr/layout.rs
def request_number(
*,
@ -304,8 +282,7 @@ def request_number(
max_count: int,
description: Callable[[int], str] | None = None, # unused on TR
) -> object:
"""Number input with + and - buttons, description, and info button."""
"""Number input with + and - buttons, description, and info button."""
# rust/src/ui/model_tr/layout.rs
def show_checklist(
@ -315,9 +292,8 @@ def show_checklist(
active: int,
button: str,
) -> object:
"""Checklist of backup steps. Active index is highlighted, previous items have check
mark next to them."""
"""Checklist of backup steps. Active index is highlighted, previous items have check
mark next to them."""
# rust/src/ui/model_tr/layout.rs
def confirm_recovery(
@ -327,25 +303,23 @@ def confirm_recovery(
button: str,
dry_run: bool,
info_button: bool, # unused on TR
show_info: bool,
) -> object:
"""Device recovery homescreen."""
"""Device recovery homescreen."""
# rust/src/ui/model_tr/layout.rs
def select_word_count(
*,
dry_run: bool, # unused on TR
) -> int | str: # TR returns str
"""Select mnemonic word count from (12, 18, 20, 24, 33)."""
"""Select mnemonic word count from (12, 18, 20, 24, 33)."""
# rust/src/ui/model_tr/layout.rs
def show_group_share_success(
*,
lines: Iterable[str],
) -> int:
"""Shown after successfully finishing a group."""
"""Shown after successfully finishing a group."""
# rust/src/ui/model_tr/layout.rs
def show_progress(
@ -354,10 +328,9 @@ def show_progress(
indeterminate: bool = False,
description: str = "",
) -> object:
"""Show progress loader. Please note that the number of lines reserved on screen for
description is determined at construction time. If you want multiline descriptions
make sure the initial description has at least that amount of lines."""
"""Show progress loader. Please note that the number of lines reserved on screen for
description is determined at construction time. If you want multiline descriptions
make sure the initial description has at least that amount of lines."""
# rust/src/ui/model_tr/layout.rs
def show_progress_coinjoin(
@ -367,9 +340,8 @@ def show_progress_coinjoin(
time_ms: int = 0,
skip_first_paint: bool = False,
) -> object:
"""Show progress loader for coinjoin. Returns CANCELLED after a specified time when
time_ms timeout is passed."""
"""Show progress loader for coinjoin. Returns CANCELLED after a specified time when
time_ms timeout is passed."""
# rust/src/ui/model_tr/layout.rs
def show_homescreen(
@ -382,7 +354,6 @@ def show_homescreen(
) -> CANCELLED:
"""Idle homescreen."""
# rust/src/ui/model_tr/layout.rs
def show_lockscreen(
*,
@ -392,30 +363,26 @@ def show_lockscreen(
) -> CANCELLED:
"""Homescreen for locked device."""
# rust/src/ui/model_tr/layout.rs
def draw_welcome_screen() -> None:
"""Show logo icon with the model name at the bottom and return."""
CONFIRMED: object
CANCELLED: object
INFO: object
# rust/src/ui/model_tt/layout.rs
def disable_animation(disable: bool) -> None:
"""Disable animations, debug builds only."""
# rust/src/ui/model_tt/layout.rs
def jpeg_info(data: bytes) -> tuple[int, int, int]:
"""Get JPEG image dimensions (width: int, height: int, mcu_height: int)."""
# rust/src/ui/model_tt/layout.rs
def jpeg_test(data: bytes) -> bool:
"""Test JPEG image."""
# rust/src/ui/model_tt/layout.rs
def confirm_action(
*,
@ -430,7 +397,6 @@ def confirm_action(
) -> object:
"""Confirm action."""
# rust/src/ui/model_tt/layout.rs
def confirm_emphasized(
*,
@ -441,7 +407,6 @@ def confirm_emphasized(
"""Confirm formatted text that has been pre-split in python. For tuples
the first component is a bool indicating whether this part is emphasized."""
# rust/src/ui/model_tt/layout.rs
def confirm_homescreen(
*,
@ -450,7 +415,6 @@ def confirm_homescreen(
) -> object:
"""Confirm homescreen."""
# rust/src/ui/model_tt/layout.rs
def confirm_blob(
*,
@ -464,7 +428,6 @@ def confirm_blob(
) -> object:
"""Confirm byte sequence data."""
# rust/src/ui/model_tt/layout.rs
def confirm_address(
*,
@ -476,7 +439,6 @@ def confirm_address(
"""Confirm address. Similar to `confirm_blob` but has corner info button
and allows left swipe which does the same thing as the button."""
# rust/src/ui/model_tt/layout.rs
def confirm_properties(
*,
@ -487,7 +449,6 @@ def confirm_properties(
"""Confirm list of key-value pairs. The third component in the tuple should be True if
the value is to be rendered as binary with monospace font, False otherwise."""
# rust/src/ui/model_tt/layout.rs
def confirm_reset_device(
*,
@ -496,7 +457,6 @@ def confirm_reset_device(
) -> object:
"""Confirm TOS before device setup."""
# rust/src/ui/model_tt/layout.rs
def show_address_details(
*,
@ -508,7 +468,6 @@ def show_address_details(
) -> object:
"""Show address details - QR code, account, path, cosigner xpubs."""
# rust/src/ui/model_tt/layout.rs
def show_spending_details(
*,
@ -519,7 +478,6 @@ def show_spending_details(
) -> object:
"""Show metadata when for outgoing transaction."""
# rust/src/ui/model_tt/layout.rs
def confirm_value(
*,
@ -534,7 +492,6 @@ def confirm_value(
) -> object:
"""Confirm value. Merge of confirm_total and confirm_output."""
# rust/src/ui/model_tt/layout.rs
def confirm_total(
*,
@ -544,7 +501,6 @@ def confirm_total(
) -> object:
"""Transaction summary. Always hold to confirm."""
# rust/src/ui/model_tt/layout.rs
def confirm_modify_output(
*,
@ -555,7 +511,6 @@ def confirm_modify_output(
) -> object:
"""Decrease or increase amount for given address."""
# rust/src/ui/model_tt/layout.rs
def confirm_modify_fee(
*,
@ -567,7 +522,6 @@ def confirm_modify_fee(
) -> object:
"""Decrease or increase transaction fee."""
# rust/src/ui/model_tt/layout.rs
def confirm_fido(
*,
@ -580,7 +534,6 @@ def confirm_fido(
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_tt/layout.rs
def show_error(
*,
@ -592,7 +545,6 @@ def show_error(
) -> object:
"""Error modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_warning(
*,
@ -604,7 +556,6 @@ def show_warning(
) -> object:
"""Warning modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_success(
*,
@ -616,7 +567,6 @@ def show_success(
) -> object:
"""Success modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_info(
*,
@ -628,12 +578,10 @@ def show_info(
) -> object:
"""Info modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_mismatch() -> object:
"""Warning modal, receiving address mismatch."""
# rust/src/ui/model_tt/layout.rs
def show_simple(
*,
@ -643,7 +591,6 @@ def show_simple(
) -> object:
"""Simple dialog with text and one button."""
# rust/src/ui/model_tt/layout.rs
def confirm_with_info(
*,
@ -655,7 +602,6 @@ def confirm_with_info(
"""Confirm given items but with third button. Always single page
without scrolling."""
# rust/src/ui/model_tt/layout.rs
def confirm_more(
*,
@ -666,7 +612,6 @@ def confirm_more(
"""Confirm long content with the possibility to go back from any page.
Meant to be used with confirm_with_info."""
# rust/src/ui/model_tt/layout.rs
def confirm_coinjoin(
*,
@ -675,7 +620,6 @@ def confirm_coinjoin(
) -> object:
"""Confirm coinjoin authorization."""
# rust/src/ui/model_tt/layout.rs
def request_pin(
*,
@ -686,7 +630,6 @@ def request_pin(
) -> str | object:
"""Request pin on device."""
# rust/src/ui/model_tt/layout.rs
def request_passphrase(
*,
@ -695,7 +638,6 @@ def request_passphrase(
) -> str | object:
"""Passphrase input keyboard."""
# rust/src/ui/model_tt/layout.rs
def request_bip39(
*,
@ -703,7 +645,6 @@ def request_bip39(
) -> str:
"""BIP39 word input keyboard."""
# rust/src/ui/model_tt/layout.rs
def request_slip39(
*,
@ -711,7 +652,6 @@ def request_slip39(
) -> str:
"""SLIP39 word input keyboard."""
# rust/src/ui/model_tt/layout.rs
def select_word(
*,
@ -720,8 +660,7 @@ def select_word(
words: Iterable[str],
) -> int:
"""Select mnemonic word from three possibilities - seed check after backup. The
iterable must be of exact size. Returns index in range `0..3`."""
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_tt/layout.rs
def show_share_words(
@ -731,7 +670,6 @@ def show_share_words(
) -> object:
"""Show mnemonic for backup. Expects the words pre-divided into individual pages."""
# rust/src/ui/model_tt/layout.rs
def request_number(
*,
@ -743,7 +681,6 @@ def request_number(
) -> object:
"""Number input with + and - buttons, description, and info button."""
# rust/src/ui/model_tt/layout.rs
def show_checklist(
*,
@ -753,8 +690,7 @@ def show_checklist(
button: str,
) -> object:
"""Checklist of backup steps. Active index is highlighted, previous items have check
mark next to them."""
mark next to them."""
# rust/src/ui/model_tt/layout.rs
def confirm_recovery(
@ -767,7 +703,6 @@ def confirm_recovery(
) -> object:
"""Device recovery homescreen."""
# rust/src/ui/model_tt/layout.rs
def select_word_count(
*,
@ -775,15 +710,10 @@ def select_word_count(
) -> int | str: # TT returns int
"""Select mnemonic word count from (12, 18, 20, 24, 33)."""
# rust/src/ui/model_tt/layout.rs
def show_group_share_success(
*,
lines: Iterable[str]
) -> int:
def show_group_share_success(*, lines: Iterable[str]) -> int:
"""Shown after successfully finishing a group."""
# rust/src/ui/model_tt/layout.rs
def show_remaining_shares(
*,
@ -791,7 +721,6 @@ def show_remaining_shares(
) -> int:
"""Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
# rust/src/ui/model_tt/layout.rs
def show_progress(
*,
@ -800,9 +729,8 @@ def show_progress(
description: str = "",
) -> object:
"""Show progress loader. Please note that the number of lines reserved on screen for
description is determined at construction time. If you want multiline descriptions
make sure the initial description has at least that amount of lines."""
description is determined at construction time. If you want multiline descriptions
make sure the initial description has at least that amount of lines."""
# rust/src/ui/model_tt/layout.rs
def show_progress_coinjoin(
@ -813,8 +741,7 @@ def show_progress_coinjoin(
skip_first_paint: bool = False,
) -> object:
"""Show progress loader for coinjoin. Returns CANCELLED after a specified time when
time_ms timeout is passed."""
time_ms timeout is passed."""
# rust/src/ui/model_tt/layout.rs
def show_homescreen(
@ -827,7 +754,6 @@ def show_homescreen(
) -> CANCELLED:
"""Idle homescreen."""
# rust/src/ui/model_tt/layout.rs
def show_lockscreen(
*,
@ -837,7 +763,6 @@ def show_lockscreen(
) -> CANCELLED:
"""Homescreen for locked device."""
# rust/src/ui/model_tt/layout.rs
def draw_welcome_screen() -> None:
"""Show logo icon with the model name at the bottom and return."""

View File

@ -56,11 +56,11 @@ async def recovery_device(msg: RecoveryDevice) -> Success:
# --------------------------------------------------------
# _continue_dialog
if not dry_run:
await confirm_reset_device("Wallet recovery", recovery=True)
await confirm_reset_device("Recover wallet", recovery=True)
else:
await confirm_action(
"confirm_seedcheck",
"Seed check",
"Backup check",
description="Do you really want to check the recovery seed?",
br_code=ButtonRequestType.ProtectCall,
)

View File

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
import storage.device as storage_device
import storage.recovery as storage_recovery
import storage.recovery_shares as storage_recovery_shares
from trezor import wire
from trezor.messages import Success
@ -62,7 +63,9 @@ async def _continue_recovery_process() -> Success:
if is_first_step:
# If we are starting recovery, ask for word count first...
# _request_word_count
await layout.homescreen_dialog("Select", "Select number of words")
await layout.homescreen_dialog(
"Continue", "Select the number of words in your backup."
)
# ask for the number of words
word_count = await layout.request_word_count(dry_run)
# ...and only then show the starting screen with word count.
@ -150,7 +153,7 @@ async def _finish_recovery(secret: bytes, backup_type: BackupType) -> Success:
storage_recovery.end_progress()
await show_success("success_recovery", "You have finished recovering your wallet.")
await show_success("success_recovery", "Wallet recovered successfully.")
return Success(message="Device recovered")
@ -181,11 +184,17 @@ async def _request_share_first_screen(word_count: int) -> None:
await _request_share_next_screen()
else:
await layout.homescreen_dialog(
"Enter share", "Enter any share", f"({word_count} words)"
"Enter share",
"Enter any share",
f"({word_count} words)",
show_info=True,
)
else: # BIP-39
await layout.homescreen_dialog(
"Enter seed", "Enter recovery seed", f"({word_count} words)"
"Continue",
"Enter your backup.",
f"({word_count} words)",
show_info=True,
)
@ -205,8 +214,16 @@ async def _request_share_next_screen() -> None:
info_func=_show_remaining_groups_and_shares,
)
else:
text = strings.format_plural("{count} more {plural}", remaining[0], "share")
await layout.homescreen_dialog("Enter share", text, "needed to enter")
still_needed_shares = remaining[0]
already_entered_shares = len(storage_recovery_shares.fetch_group(0))
overall_needed = still_needed_shares + already_entered_shares
entered = (
f"{already_entered_shares} of {overall_needed} shares entered successfully."
)
needed = strings.format_plural(
"{count} more {plural} needed.", still_needed_shares, "share"
)
await layout.homescreen_dialog("Enter share", entered, needed)
async def _show_remaining_groups_and_shares() -> None:
@ -214,7 +231,6 @@ async def _show_remaining_groups_and_shares() -> None:
Show info dialog for Slip39 Advanced - what shares are to be entered.
"""
from trezor.crypto import slip39
import storage.recovery_shares as storage_recovery_shares
shares_remaining = storage_recovery.fetch_slip39_remaining_shares()
# should be stored at this point

View File

@ -20,8 +20,8 @@ async def _confirm_abort(dry_run: bool = False) -> None:
if dry_run:
await confirm_action(
"abort_recovery",
"Abort seed check",
description="Do you really want to abort the seed check?",
"Abort backup check",
description="Do you really want to abort the backup check?",
br_code=ButtonRequestType.ProtectCall,
)
else:
@ -41,9 +41,6 @@ async def request_mnemonic(
from . import word_validity
from trezor.ui.layouts.common import button_request
from trezor.ui.layouts.recovery import request_word
from trezor.ui.layouts import mnemonic_word_entering
await mnemonic_word_entering()
await button_request("mnemonic", code=ButtonRequestType.MnemonicInput)
@ -60,7 +57,8 @@ async def request_mnemonic(
# show_share_already_added
await show_recovery_warning(
"warning_known_share",
"Share already entered, please enter a different share.",
"Share already entered",
"Please enter a different share.",
)
return None
except word_validity.IdentifierMismatch:
@ -74,7 +72,8 @@ async def request_mnemonic(
# show_group_threshold_reached
await show_recovery_warning(
"warning_group_threshold",
"Threshold of this group has been reached. Input share from different group.",
"Group threshold reached.",
"Enter share from a different group.",
)
return None
@ -97,19 +96,21 @@ async def show_dry_run_result(result: bool, is_slip39: bool) -> None:
text = "The entered recovery shares are valid but do not match what is currently in the device."
else:
text = "The entered recovery seed is valid but does not match the one in the device."
await show_recovery_warning("warning_dry_recovery", text, button="Continue")
await show_recovery_warning("warning_dry_recovery", "", text, button="Continue")
async def show_invalid_mnemonic(word_count: int) -> None:
if backup_types.is_slip39_word_count(word_count):
await show_recovery_warning(
"warning_invalid_share",
"You have entered an invalid recovery share.",
"Invalid recovery share entered.",
"Please try again",
)
else:
await show_recovery_warning(
"warning_invalid_seed",
"You have entered an invalid recovery seed.",
"Invalid recovery seed entered.",
"Please try again",
)
@ -118,6 +119,7 @@ async def homescreen_dialog(
text: str,
subtext: str | None = None,
info_func: Callable | None = None,
show_info: bool = False,
) -> None:
from .recover import RecoveryAborted
import storage.recovery as storage_recovery
@ -126,7 +128,9 @@ async def homescreen_dialog(
while True:
dry_run = storage_recovery.is_dry_run()
if await continue_recovery(button_label, text, subtext, info_func, dry_run):
if await continue_recovery(
button_label, text, subtext, info_func, dry_run, show_info
):
# go forward in the recovery process
break
# user has chosen to abort, confirm the choice

View File

@ -1,7 +1,7 @@
from typing import TYPE_CHECKING
import storage.recovery as storage_recovery
import storage.recovery_shares
import storage.recovery_shares as storage_recovery_shares
from trezor.crypto import slip39
if TYPE_CHECKING:
@ -42,7 +42,7 @@ def process_slip39(words: str) -> tuple[bytes | None, slip39.Share]:
storage_recovery.set_slip39_iteration_exponent(share.iteration_exponent)
storage_recovery.set_slip39_identifier(share.identifier)
storage_recovery.set_slip39_remaining_shares(share.threshold - 1, group_index)
storage.recovery_shares.set(share.index, group_index, words)
storage_recovery_shares.set(share.index, group_index, words)
# if share threshold and group threshold are 1
# we can calculate the secret right away
@ -58,7 +58,7 @@ def process_slip39(words: str) -> tuple[bytes | None, slip39.Share]:
raise RuntimeError("Slip39: Share identifiers do not match")
if share.iteration_exponent != storage_recovery.get_slip39_iteration_exponent():
raise RuntimeError("Slip39: Share exponents do not match")
if storage.recovery_shares.get(share.index, group_index):
if storage_recovery_shares.get(share.index, group_index):
raise RuntimeError("Slip39: This mnemonic was already entered")
if share.group_count != storage_recovery.get_slip39_group_count():
raise RuntimeError("Slip39: Group count does not match")
@ -68,7 +68,7 @@ def process_slip39(words: str) -> tuple[bytes | None, slip39.Share]:
)
storage_recovery.set_slip39_remaining_shares(remaining_for_share - 1, group_index)
remaining[group_index] = remaining_for_share - 1
storage.recovery_shares.set(share.index, group_index, words)
storage_recovery_shares.set(share.index, group_index, words)
if remaining.count(0) < share.group_threshold:
# we need more shares
@ -79,11 +79,11 @@ def process_slip39(words: str) -> tuple[bytes | None, slip39.Share]:
for i, r in enumerate(remaining):
# if we have multiple groups pass only the ones with threshold reached
if r == 0:
group = storage.recovery_shares.fetch_group(i)
group = storage_recovery_shares.fetch_group(i)
mnemonics.extend(group)
else:
# in case of slip39 basic we only need the first and only group
mnemonics = storage.recovery_shares.fetch_group(0)
mnemonics = storage_recovery_shares.fetch_group(0)
_, _, secret = slip39.recover_ems(mnemonics)
return secret, share
@ -111,7 +111,7 @@ def fetch_previous_mnemonics() -> list[list[str]] | None:
if not storage_recovery.get_slip39_group_count():
return None
for i in range(storage_recovery.get_slip39_group_count()):
mnemonics.append(storage.recovery_shares.fetch_group(i))
mnemonics.append(storage_recovery_shares.fetch_group(i))
if not any(p for p in mnemonics):
return None
return mnemonics

View File

@ -10,13 +10,13 @@ _NAMESPACE = common.APP_RECOVERY
_IN_PROGRESS = const(0x00) # bool
_DRY_RUN = const(0x01) # bool
_SLIP39_IDENTIFIER = const(0x03) # bytes
_SLIP39_THRESHOLD = const(0x04) # int
_REMAINING = const(0x05) # int
_SLIP39_ITERATION_EXPONENT = const(0x06) # int
_SLIP39_GROUP_COUNT = const(0x07) # int
# Deprecated Keys:
# _WORD_COUNT = const(0x02) # int
# _SLIP39_THRESHOLD = const(0x04) # int
# fmt: on
# Default values:
@ -130,7 +130,6 @@ def end_progress() -> None:
_IN_PROGRESS,
_DRY_RUN,
_SLIP39_IDENTIFIER,
_SLIP39_THRESHOLD,
_REMAINING,
_SLIP39_ITERATION_EXPONENT,
_SLIP39_GROUP_COUNT,

View File

@ -399,6 +399,16 @@ async def confirm_reset_device(
)
)
if recovery:
await confirm_action(
ctx,
"recover_device",
title,
description="It is safe to eject your Trezor anytime and continue later.",
verb="CONTINUE",
br_code=ButtonRequestType.ProtectCall,
)
async def confirm_backup() -> bool:
br_type = "backup_device"
@ -597,21 +607,23 @@ async def show_error_and_raise(
raise exc
def show_warning(
async def show_warning(
br_type: str,
content: str,
subheader: str | None = None,
button: str = "Try again",
button: str = "CONTINUE",
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> Awaitable[None]:
return _show_modal(
) -> None:
await interact(
RustLayout(
trezorui2.show_warning( # type: ignore [Argument missing for parameter "title"]
button=button.upper(),
warning=content, # type: ignore [No parameter named "warning"]
description=subheader or "",
)
),
br_type,
"",
subheader or "WARNING",
content,
button_confirm=button,
button_cancel=None,
br_code=br_code,
br_code,
)
@ -1160,26 +1172,6 @@ async def confirm_reenter_pin(
)
async def show_error(
br_type: str,
title: str,
description: str,
button: str,
br_code: ButtonRequestType = BR_TYPE_OTHER,
) -> None:
await interact(
RustLayout(
trezorui2.show_error(
title=title,
description=description,
button=button,
)
),
br_type,
br_code,
)
async def confirm_multiple_pages_texts(
br_type: str,
title: str,
@ -1207,7 +1199,7 @@ async def pin_mismatch_popup(
) -> None:
description = "wipe codes" if is_wipe_code else "PINs"
br_code = "wipe_code_mismatch" if is_wipe_code else "pin_mismatch"
return await show_error(
return await show_warning(
br_code,
f"Entered {description} do not match!",
"Please check again.",
@ -1259,14 +1251,3 @@ async def confirm_set_new_pin(
"CONTINUE",
br_code,
)
async def mnemonic_word_entering() -> None:
await confirm_action(
"request_word",
"WORD ENTERING",
description="You'll only have to select the first 2-3 letters.",
verb="CONTINUE",
verb_cancel=None,
br_code=ButtonRequestType.MnemonicInput,
)

View File

@ -4,12 +4,11 @@ from trezor.enums import ButtonRequestType
import trezorui2
from ..common import button_request, interact
from ..common import interact
from . import RustLayout, raise_if_not_confirmed, show_warning
async def request_word_count(dry_run: bool) -> int:
await button_request("word_count", code=ButtonRequestType.MnemonicWordCount)
count = await interact(
RustLayout(trezorui2.select_word_count(dry_run=dry_run)),
"word_count",
@ -66,6 +65,7 @@ async def continue_recovery(
subtext: str | None,
info_func: Callable | None,
dry_run: bool,
show_info: bool = False,
) -> bool:
# TODO: implement info_func?
# There is very limited space on the screen
@ -82,6 +82,7 @@ async def continue_recovery(
button=button_label.upper(),
info_button=False,
dry_run=dry_run,
show_info=show_info, # type: ignore [No parameter named "show_info"]
)
)
result = await interact(

View File

@ -6,7 +6,7 @@ from trezor.wire import ActionCancelled
import trezorui2
from ..common import interact
from . import RustLayout, confirm_action, show_error
from . import RustLayout, confirm_action, show_warning
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
@ -247,7 +247,7 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int:
async def show_warning_backup(slip39: bool) -> None:
await show_error(
await show_warning(
"backup_warning",
"REMEMBER",
"Never make a digital copy of your backup or upload it online!",
@ -275,7 +275,7 @@ async def show_reset_warning(
button: str = "TRY AGAIN",
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> None:
await show_error(
await show_warning(
ctx,
br_type,
button.upper(),

View File

@ -1238,17 +1238,3 @@ async def confirm_set_new_pin(
br_code,
)
)
# await confirm_action(
# ctx,
# br_type,
# title,
# description=description,
# verb="TURN ON",
# br_code=br_code,
# )
async def mnemonic_word_entering() -> None:
"""Not supported for TT."""
pass

View File

@ -102,6 +102,7 @@ async def continue_recovery(
subtext: str | None,
info_func: Callable | None,
dry_run: bool,
show_info: bool = False, # unused on TT
) -> bool:
from ..common import button_request

View File

@ -42,12 +42,16 @@ def confirm_recovery(debug: "DebugLink") -> None:
if debug.model == "T":
if not debug.legacy_ui and not debug.legacy_debug:
layout = debug.wait_layout()
assert layout.title().startswith("WALLET RECOVERY")
assert layout.title().startswith(
("WALLET RECOVERY", "RECOVER WALLET", "BACKUP CHECK")
)
debug.click(buttons.OK, wait=True)
elif debug.model == "R":
layout = debug.wait_layout()
assert layout.title() == "WALLET RECOVERY"
assert layout.title() == "RECOVER WALLET"
debug.press_right(wait=True)
layout = debug.press_right(wait=True)
assert "safe to eject" in layout.text_content()
debug.press_right()
@ -66,8 +70,11 @@ def select_number_of_words(
elif debug.legacy_debug:
assert "SelectWordCount" in layout.json_str
else:
# Two title options
assert layout.title() in ("SEED CHECK", "WALLET RECOVERY")
assert layout.title() in (
"WALLET RECOVERY",
"BACKUP CHECK",
"RECOVER WALLET",
)
# click the number
word_option_offset = 6
@ -96,10 +103,12 @@ def select_number_of_words(
if num_of_words in (20, 33):
assert "Enter any share" in layout.text_content()
else:
assert "Enter recovery seed" in layout.text_content()
assert "Enter your backup" in layout.text_content()
def enter_share(debug: "DebugLink", share: str) -> "LayoutContent":
def enter_share(
debug: "DebugLink", share: str, is_first: bool = True
) -> "LayoutContent":
if debug.model == "T":
layout = debug.click(buttons.OK, wait=True)
@ -115,10 +124,12 @@ def enter_share(debug: "DebugLink", share: str) -> "LayoutContent":
return layout
elif debug.model == "R":
assert "RECOVER WALLET" in debug.wait_layout().title()
layout = debug.press_right(wait=True)
assert layout.title() == "WORD ENTERING"
layout = debug.press_right(wait=True)
if is_first:
# Word entering info
debug.press_right()
layout = debug.press_right(wait=True)
assert "Slip39Entry" in layout.all_components()
for word in share.split(" "):
@ -133,39 +144,32 @@ def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
layout = debug.read_layout()
expected_text = "Enter any share"
remaining = len(shares)
for share in shares:
for index, share in enumerate(shares):
assert expected_text in layout.text_content()
layout = enter_share(debug, share)
layout = enter_share(debug, share, is_first=index == 0)
remaining -= 1
expected_text = f"{remaining} more share"
assert "You have finished recovering your wallet" in layout.text_content()
assert "Wallet recovered successfully" in layout.text_content()
def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None:
assert "Enter" in debug.read_layout().text_content()
if debug.model == "T":
assert "Enter" in debug.read_layout().text_content()
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "MnemonicKeyboard"
for word in seed_words:
layout = enter_word(debug, word, is_slip39=False)
assert "You have finished recovering your wallet" in layout.text_content()
elif debug.model == "R":
assert "Enter" in debug.read_layout().text_content()
layout = debug.press_right(wait=True)
assert layout.title() == "WORD ENTERING"
assert "RECOVER WALLET" in layout.title()
debug.press_right()
layout = debug.press_right(wait=True)
assert "Bip39Entry" in layout.all_components()
for word in seed_words:
layout = enter_word(debug, word, is_slip39=False)
for word in seed_words:
layout = enter_word(debug, word, is_slip39=False)
assert "You have finished recovering your wallet" in layout.text_content()
assert "Wallet recovered successfully" in layout.text_content() # type: ignore
def finalize(debug: "DebugLink") -> None:

View File

@ -301,7 +301,8 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
assert layout.main_component() == "MnemonicKeyboard"
elif debug.model == "R":
layout = debug.press_right(wait=True)
assert "WORD ENTERING" in layout.title()
assert "RECOVER WALLET" in layout.title()
debug.press_right()
layout = debug.press_right(wait=True)
assert "Slip39Entry" in layout.all_components()
@ -337,7 +338,8 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
assert layout.main_component() == "MnemonicKeyboard"
elif debug.model == "R":
layout = debug.press_right(wait=True)
assert "WORD ENTERING" in layout.title()
assert "RECOVER WALLET" in layout.title()
debug.press_right()
layout = debug.press_right(wait=True)
assert "Slip39Entry" in layout.all_components()

View File

@ -227,16 +227,15 @@ def recovery_enter_shares_tr(
debug.input(str(word_count))
# Homescreen - proceed to share entry
yield
assert "Enter any share" in debug.wait_layout().text_content()
debug.press_right()
debug.press_right()
debug.press_yes()
# Enter shares
for share in shares:
br = yield
assert br.code == ButtonRequestType.RecoveryHomepage
# Word entering
yield
debug.press_yes()
assert br.code == ButtonRequestType.MnemonicInput
# Enter mnemonic words
for word in share.split(" "):
@ -368,7 +367,7 @@ def read_and_confirm_mnemonic_tr(
return " ".join(mnemonic)
def click_info_button(debug: "DebugLink"):
def click_info_button_tt(debug: "DebugLink"):
"""Click Shamir backup info button and return back."""
debug.press_info()
yield # Info screen with text

View File

@ -30,7 +30,6 @@ def test_tt_pin_passphrase(client: Client):
with client:
IF = InputFlowBip39RecoveryPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client,
pin_protection=True,
@ -51,7 +50,6 @@ def test_tt_nopin_nopassphrase(client: Client):
with client:
IF = InputFlowBip39RecoveryNoPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client,
pin_protection=False,

View File

@ -117,8 +117,6 @@ def test_noabort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_ask_word_number(client: Client):
if client.features.model == "R":
pytest.skip("Flow is not working correctly for TR")
with client:
IF = InputFlowSlip39BasicRecoveryRetryFirst(client)
client.set_input_flow(IF.get())

View File

@ -23,7 +23,7 @@ from trezorlib.debuglink import (
from . import buttons
from .common import (
check_pin_backoff_time,
click_info_button,
click_info_button_tt,
click_through,
read_and_confirm_mnemonic,
recovery_enter_shares,
@ -396,14 +396,14 @@ class InputFlowPaymentRequestDetails(InputFlowBase):
self.debug.press_info()
yield # confirm first output
assert self.outputs[0].address[:16] in self.layout().text_content()
assert self.outputs[0].address[:16] in self.layout().text_content() # type: ignore
self.debug.press_yes()
yield # confirm first output
self.debug.wait_layout()
self.debug.press_yes()
yield # confirm second output
assert self.outputs[1].address[:16] in self.layout().text_content()
assert self.outputs[1].address[:16] in self.layout().text_content() # type: ignore
self.debug.press_yes()
yield # confirm second output
self.debug.wait_layout()
@ -503,12 +503,12 @@ class InputFlowSignTxInformation(InputFlowBase):
def input_flow_tt(self) -> GeneratorType:
content = yield from sign_tx_go_to_info(self.client)
self.assert_content(content)
self.client.debug.press_yes()
self.debug.press_yes()
def input_flow_tr(self) -> GeneratorType:
content = yield from sign_tx_go_to_info_tr(self.client)
self.assert_content(content.lower())
self.client.debug.press_yes()
self.debug.press_yes()
class InputFlowSignTxInformationMixed(InputFlowBase):
@ -524,12 +524,12 @@ class InputFlowSignTxInformationMixed(InputFlowBase):
def input_flow_tt(self) -> GeneratorType:
content = yield from sign_tx_go_to_info(self.client)
self.assert_content(content)
self.client.debug.press_yes()
self.debug.press_yes()
def input_flow_tr(self) -> GeneratorType:
content = yield from sign_tx_go_to_info_tr(self.client)
self.assert_content(content.lower())
self.client.debug.press_yes()
self.debug.press_yes()
class InputFlowSignTxInformationCancel(InputFlowBase):
@ -538,11 +538,11 @@ class InputFlowSignTxInformationCancel(InputFlowBase):
def input_flow_tt(self) -> GeneratorType:
yield from sign_tx_go_to_info(self.client)
self.client.debug.press_no()
self.debug.press_no()
def input_flow_tr(self) -> GeneratorType:
yield from sign_tx_go_to_info_tr(self.client)
self.client.debug.press_left()
self.debug.press_left()
class InputFlowSignTxInformationReplacement(InputFlowBase):
@ -551,15 +551,15 @@ class InputFlowSignTxInformationReplacement(InputFlowBase):
def input_flow_tt(self) -> GeneratorType:
yield # confirm txid
self.client.debug.press_yes()
self.debug.press_yes()
yield # confirm address
self.client.debug.press_yes()
self.debug.press_yes()
# go back to address
self.client.debug.press_no()
self.debug.press_no()
# confirm address
self.client.debug.press_yes()
self.debug.press_yes()
yield # confirm amount
self.client.debug.press_yes()
self.debug.press_yes()
yield # transaction summary, press info
self.client.debug.click(buttons.CORNER_BUTTON, wait=True)
@ -568,16 +568,16 @@ class InputFlowSignTxInformationReplacement(InputFlowBase):
def input_flow_tr(self) -> GeneratorType:
yield # confirm txid
self.client.debug.press_right()
self.client.debug.press_right()
self.debug.press_right()
self.debug.press_right()
yield # confirm address
self.client.debug.press_right()
self.client.debug.press_right()
self.client.debug.press_right()
self.debug.press_right()
self.debug.press_right()
self.debug.press_right()
yield # confirm amount
self.client.debug.press_right()
self.client.debug.press_right()
self.client.debug.press_right()
self.debug.press_right()
self.debug.press_right()
self.debug.press_right()
def lock_time_input_flow_tt(
@ -1015,13 +1015,13 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
yield # 1. Checklist
self.debug.press_yes()
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # 2. Number of shares (5)
self.debug.press_yes()
yield # 3. Checklist
self.debug.press_yes()
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # 4. Threshold (3)
self.debug.press_yes()
yield # 5. Checklist
@ -1037,21 +1037,21 @@ class InputFlowSlip39BasicBackup(InputFlowBase):
self.debug.press_yes()
def input_flow_tr(self) -> GeneratorType:
yield # Checklist
yield # 1. Checklist
self.debug.press_yes()
yield # Number of shares info
yield # 1.5 Number of shares info
self.debug.press_yes()
yield # Number of shares (5)
yield # 2. Number of shares (5)
self.debug.input("5")
yield # Checklist
yield # 3. Checklist
self.debug.press_yes()
yield # Threshold info
yield # 3.5 Threshold info
self.debug.press_yes()
yield # Threshold (3)
yield # 4. Threshold (3)
self.debug.input("3")
yield # Checklist
yield # 5. Checklist
self.debug.press_yes()
yield # Confirm show seeds
yield # 6. Confirm show seeds
self.debug.press_yes()
# Mnemonic phrases
@ -1143,24 +1143,24 @@ class InputFlowSlip39AdvancedBackup(InputFlowBase):
yield # 1. Checklist
self.debug.press_yes()
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # 2. Set and confirm group count
self.debug.press_yes()
yield # 3. Checklist
self.debug.press_yes()
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # 4. Set and confirm group threshold
self.debug.press_yes()
yield # 5. Checklist
self.debug.press_yes()
for _ in range(5): # for each of 5 groups
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # Set & Confirm number of shares
self.debug.press_yes()
if self.click_info:
yield from click_info_button(self.debug)
yield from click_info_button_tt(self.debug)
yield # Set & confirm share threshold value
self.debug.press_yes()
yield # Confirm show seeds
@ -1266,7 +1266,9 @@ class InputFlowSlip39AdvancedResetRecovery(InputFlowBase):
self.debug.press_yes()
def enter_recovery_seed_dry_run(debug: DebugLink, mnemonic: list[str]) -> GeneratorType:
def enter_recovery_seed_dry_run_tt(
debug: DebugLink, mnemonic: list[str]
) -> GeneratorType:
yield
assert "check the recovery seed" in debug.wait_layout().text_content()
debug.click(buttons.OK)
@ -1284,7 +1286,7 @@ def enter_recovery_seed_dry_run(debug: DebugLink, mnemonic: list[str]) -> Genera
debug.click(buttons.grid34(index % 3, index // 3))
yield
assert "Enter recovery seed" in debug.wait_layout().text_content()
assert "Enter your backup" in debug.wait_layout().text_content()
debug.click(buttons.OK)
yield
@ -1299,7 +1301,7 @@ class InputFlowBip39RecoveryDryRun(InputFlowBase):
self.mnemonic = mnemonic
def input_flow_tt(self) -> GeneratorType:
yield from enter_recovery_seed_dry_run(self.debug, self.mnemonic)
yield from enter_recovery_seed_dry_run_tt(self.debug, self.mnemonic)
yield
self.debug.wait_layout()
@ -1310,33 +1312,9 @@ class InputFlowBip39RecoveryDryRun(InputFlowBase):
assert "check the recovery seed" in self.layout().text_content()
self.debug.press_yes()
yield
assert "number of words" in self.layout().text_content()
self.debug.press_yes()
yield from enter_recovery_seed_tr(self.debug, self.mnemonic)
yield
yield
assert "NUMBER OF WORDS" in self.layout().title()
word_options = (12, 18, 20, 24, 33)
index = word_options.index(len(self.mnemonic))
for _ in range(index):
self.debug.press_right()
self.debug.input(str(len(self.mnemonic)))
yield
assert "Enter recovery seed" in self.layout().text_content()
self.debug.press_yes()
yield
self.debug.press_yes()
yield
for index, word in enumerate(self.mnemonic):
assert "WORD" in self.layout().title()
assert str(index + 1) in self.layout().title()
self.debug.input(word)
yield
self.debug.press_right()
self.debug.press_yes()
@ -1346,11 +1324,11 @@ class InputFlowBip39RecoveryDryRunInvalid(InputFlowBase):
def input_flow_tt(self) -> GeneratorType:
mnemonic = ["stick"] * 12
yield from enter_recovery_seed_dry_run(self.debug, mnemonic)
yield from enter_recovery_seed_dry_run_tt(self.debug, mnemonic)
br = yield
assert br.code == messages.ButtonRequestType.Warning
assert "invalid recovery seed" in self.layout().text_content()
assert "Invalid recovery seed" in self.layout().text_content()
self.debug.click(buttons.OK)
yield # retry screen
@ -1358,7 +1336,7 @@ class InputFlowBip39RecoveryDryRunInvalid(InputFlowBase):
self.debug.click(buttons.CANCEL)
yield
assert "ABORT SEED CHECK" == self.layout().title()
assert "ABORT BACKUP CHECK" == self.layout().title()
self.debug.click(buttons.OK)
def input_flow_tr(self) -> GeneratorType:
@ -1366,33 +1344,13 @@ class InputFlowBip39RecoveryDryRunInvalid(InputFlowBase):
assert "check the recovery seed" in self.layout().text_content()
self.debug.press_right()
yield
assert "number of words" in self.layout().text_content()
self.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in self.layout().title()
# select 12 words
self.debug.press_middle()
yield
assert "Enter recovery seed" in self.layout().text_content()
self.debug.press_yes()
yield
assert "WORD ENTERING" in self.layout().title()
self.debug.press_yes()
yield
for _ in range(12):
assert "WORD" in self.layout().title()
self.debug.input("stick")
mnemonic = ["stick"] * 12
yield from enter_recovery_seed_tr(self.debug, mnemonic)
br = yield
assert br.code == messages.ButtonRequestType.Warning
assert "invalid recovery seed" in self.layout().text_content()
self.debug.press_right()
assert "Invalid recovery seed" in self.layout().text_content()
self.debug.press_middle()
yield # retry screen
assert "number of words" in self.layout().text_content()
@ -1403,7 +1361,7 @@ class InputFlowBip39RecoveryDryRunInvalid(InputFlowBase):
self.debug.press_right()
def bip39_recovery_possible_pin(
def bip39_recovery_possible_pin_tt(
debug: DebugLink, mnemonic: list[str], pin: Optional[str]
) -> GeneratorType:
yield
@ -1429,7 +1387,7 @@ def bip39_recovery_possible_pin(
debug.input(str(len(mnemonic)))
yield
assert "Enter recovery seed" in debug.wait_layout().text_content()
assert "Enter your backup" in debug.wait_layout().text_content()
debug.press_yes()
yield
@ -1438,65 +1396,76 @@ def bip39_recovery_possible_pin(
debug.input(word)
yield
assert (
"You have finished recovering your wallet."
in debug.wait_layout().text_content()
)
assert "Wallet recovered successfully" in debug.wait_layout().text_content()
debug.press_yes()
def bip39_recovery_possible_pin_tr(
debug: DebugLink, mnemonic: list[str], pin: Optional[str]
) -> GeneratorType:
yield
assert "By continuing you agree" in debug.wait_layout().text_content()
debug.press_right()
assert "trezor.io/tos" in debug.wait_layout().text_content()
debug.press_yes()
yield
assert "safe to eject" in debug.wait_layout().text_content()
debug.press_yes()
# PIN when requested
if pin is not None:
yield
debug.input("654")
yield
assert "re-enter to confirm" in debug.wait_layout().text_content()
debug.press_right()
yield
debug.input("654")
yield from enter_recovery_seed_tr(debug, mnemonic)
yield
assert "Wallet recovered successfully" in debug.wait_layout().text_content()
debug.press_yes()
def enter_recovery_seed_tr(debug: DebugLink, mnemonic: list[str]) -> GeneratorType:
yield
assert "number of words" in debug.wait_layout().text_content()
debug.press_yes()
yield
assert "NUMBER OF WORDS" in debug.wait_layout().title()
debug.input(str(len(mnemonic)))
yield
assert "Enter your backup" in debug.wait_layout().text_content()
# Paginate to see info
debug.press_right()
debug.press_right()
debug.press_yes()
yield
for index, word in enumerate(mnemonic):
title = debug.wait_layout().title()
assert "WORD" in title
assert str(index + 1) in title
debug.input(word)
class InputFlowBip39RecoveryPIN(InputFlowBase):
def __init__(self, client: Client, mnemonic: list[str]):
super().__init__(client)
self.mnemonic = mnemonic
def input_flow_tt(self) -> GeneratorType:
yield from bip39_recovery_possible_pin(self.debug, self.mnemonic, pin="654")
yield from bip39_recovery_possible_pin_tt(self.debug, self.mnemonic, pin="654")
def input_flow_tr(self) -> GeneratorType:
yield
assert "By continuing you agree" in self.layout().text_content()
self.debug.press_right()
assert "trezor.io/tos" in self.layout().text_content()
self.debug.press_yes()
yield
self.debug.input("654")
yield
assert "re-enter PIN" in self.layout().text_content()
self.debug.press_right()
yield
self.debug.input("654")
yield
assert "number of words" in self.layout().text_content()
self.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in self.layout().title()
self.debug.input(str(len(self.mnemonic)))
yield
assert "Enter recovery seed" in self.layout().text_content()
self.debug.press_yes()
yield
assert "WORD ENTERING" in self.layout().title()
self.debug.press_right()
yield
for word in self.mnemonic:
assert "WORD" in self.layout().title()
self.debug.input(word)
yield
assert (
"You have finished recovering your wallet." in self.layout().text_content()
)
self.debug.press_yes()
yield from bip39_recovery_possible_pin_tr(self.debug, self.mnemonic, pin="654")
class InputFlowBip39RecoveryNoPIN(InputFlowBase):
@ -1505,28 +1474,10 @@ class InputFlowBip39RecoveryNoPIN(InputFlowBase):
self.mnemonic = mnemonic
def input_flow_tt(self) -> GeneratorType:
yield from bip39_recovery_possible_pin(self.debug, self.mnemonic, pin=None)
yield from bip39_recovery_possible_pin_tt(self.debug, self.mnemonic, pin=None)
def input_flow_tr(self) -> GeneratorType:
yield # Confirm recovery
self.debug.press_yes()
yield # Homescreen
self.debug.press_yes()
yield # Enter word count
self.debug.input(str(len(self.mnemonic)))
yield # Homescreen
self.debug.press_yes()
yield # Homescreen
self.debug.press_yes()
yield # Enter words
for word in self.mnemonic:
self.debug.input(word)
yield # confirm success
self.debug.press_yes()
yield
yield from bip39_recovery_possible_pin_tr(self.debug, self.mnemonic, pin=None)
class InputFlowSlip39AdvancedRecoveryDryRun(InputFlowBase):
@ -1541,6 +1492,18 @@ class InputFlowSlip39AdvancedRecoveryDryRun(InputFlowBase):
yield from recovery_enter_shares(self.debug, self.shares, groups=True)
def confirm_recovery(debug: DebugLink) -> GeneratorType:
if debug.model == "T":
yield # Confirm Recovery
debug.press_yes()
elif debug.model == "R":
yield # Confirm Recovery
debug.press_right()
debug.press_yes()
yield # Safe to eject
debug.press_yes()
class InputFlowSlip39AdvancedRecovery(InputFlowBase):
def __init__(self, client: Client, shares: list[str], click_info: bool):
super().__init__(client)
@ -1548,9 +1511,7 @@ class InputFlowSlip39AdvancedRecovery(InputFlowBase):
self.click_info = click_info
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
# Proceed with recovery
yield from confirm_recovery(self.debug)
yield from recovery_enter_shares(
self.debug, self.shares, groups=True, click_info=self.click_info
)
@ -1561,8 +1522,7 @@ class InputFlowSlip39AdvancedRecoveryAbort(InputFlowBase):
super().__init__(client)
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield from confirm_recovery(self.debug)
yield # Homescreen - abort process
self.debug.press_no()
yield # Homescreen - confirm abort
@ -1575,11 +1535,12 @@ class InputFlowSlip39AdvancedRecoveryNoAbort(InputFlowBase):
self.shares = shares
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield from confirm_recovery(self.debug)
yield # Homescreen - abort process
self.debug.press_no()
yield # Homescreen - go back to process
if self.debug.model == "R":
self.debug.press_right()
self.debug.press_no()
yield from recovery_enter_shares(self.debug, self.shares, groups=True)
@ -1590,18 +1551,9 @@ class InputFlowSlip39AdvancedRecoveryTwoSharesWarning(InputFlowBase):
self.first_share = first_share
self.second_share = second_share
def input_flow_tt(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input(str(len(self.first_share)))
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
for word in self.first_share:
self.debug.input(word)
def input_flow_common(self) -> GeneratorType:
yield from confirm_recovery(self.debug)
yield from slip39_recovery_setup_and_first_share(self.debug, self.first_share)
yield # Continue to next share
self.debug.press_yes()
@ -1613,38 +1565,6 @@ class InputFlowSlip39AdvancedRecoveryTwoSharesWarning(InputFlowBase):
br = yield
assert br.code == messages.ButtonRequestType.Warning
self.client.cancel()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input(str(len(self.first_share)))
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
self.debug.press_yes()
yield # Enter first share
for word in self.first_share:
self.debug.input(word)
yield # Continue to next share
self.debug.press_yes()
yield # Homescreen - next share
self.debug.press_yes()
yield # Homescreen - next share
self.debug.press_yes()
yield # Enter next share
for word in self.second_share:
self.debug.input(word)
yield
br = yield
assert br.code == messages.ButtonRequestType.Warning
self.debug.press_right()
self.debug.press_yes()
yield
@ -1655,6 +1575,11 @@ def slip39_recovery_possible_pin(
debug: DebugLink, shares: list[str], pin: Optional[str]
) -> GeneratorType:
yield # Confirm Recovery/Dryrun
if debug.model == "R" and "BACKUP CHECK" not in debug.wait_layout().title():
# dryruns do not have extra dialogs
debug.press_right()
debug.press_yes()
yield
debug.press_yes()
if pin is not None:
@ -1694,8 +1619,7 @@ class InputFlowSlip39BasicRecoveryAbort(InputFlowBase):
super().__init__(client)
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield from confirm_recovery(self.debug)
yield # Homescreen - abort process
self.debug.press_no()
yield # Homescreen - confirm abort
@ -1708,11 +1632,12 @@ class InputFlowSlip39BasicRecoveryNoAbort(InputFlowBase):
self.shares = shares
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
yield from confirm_recovery(self.debug)
yield # Homescreen - abort process
self.debug.press_no()
yield # Homescreen - go back to process
if self.debug.model == "R":
self.debug.press_right()
self.debug.press_no()
# run recovery flow
yield from recovery_enter_shares(self.debug, self.shares)
@ -1726,6 +1651,9 @@ def slip39_recovery_setup_and_first_share(
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
if debug.model == "R":
debug.press_right(wait=True)
debug.press_right(wait=True)
debug.press_yes()
yield # Enter first share
for word in first_share:
@ -1736,9 +1664,8 @@ class InputFlowSlip39BasicRecoveryRetryFirst(InputFlowBase):
def __init__(self, client: Client):
super().__init__(client)
def input_flow_tt(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
def input_flow_common(self) -> GeneratorType:
yield from confirm_recovery(self.debug)
first_share = ["slush"] * 20
yield from slip39_recovery_setup_and_first_share(self.debug, first_share)
@ -1757,55 +1684,8 @@ class InputFlowSlip39BasicRecoveryRetryFirst(InputFlowBase):
yield # Homescreen
self.debug.press_no()
yield # Confirm abort
self.debug.press_yes()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_right()
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input("20")
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
self.debug.press_yes()
for _ in range(20):
self.debug.input("slush")
yield
# assert br.code == messages.ButtonRequestType.Warning
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input("33")
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield
for _ in range(33):
self.debug.input("slush")
yield
self.debug.press_yes()
yield
self.debug.press_no()
yield
self.debug.press_right()
yield
self.debug.press_right()
yield
self.debug.press_right()
yield
if self.debug.model == "R":
self.debug.press_right(wait=True)
self.debug.press_yes()
@ -1814,9 +1694,8 @@ class InputFlowSlip39BasicRecoveryRetrySecond(InputFlowBase):
super().__init__(client)
self.shares = shares
def input_flow_tt(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
def input_flow_common(self) -> GeneratorType:
yield from confirm_recovery(self.debug)
# First valid share
first_share = self.shares[0].split(" ")
@ -1842,45 +1721,8 @@ class InputFlowSlip39BasicRecoveryRetrySecond(InputFlowBase):
yield # More shares needed
self.debug.press_no()
yield # Confirm abort
self.debug.press_yes()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_right()
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input("20")
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
self.debug.press_yes()
yield # Enter first share
share = self.shares[0].split(" ")
for word in share:
self.debug.input(word)
yield # More shares needed
self.debug.press_yes()
yield # Enter another share
share = share[:3] + ["slush"] * 17
for word in share:
self.debug.input(word)
yield # Invalid share
# assert br.code == messages.ButtonRequestType.Warning
self.debug.press_yes()
yield # Proceed to next share
share = self.shares[1].split(" ")
for word in share:
self.debug.input(word)
yield # More shares needed
self.debug.press_no()
yield # Confirm abort
if self.debug.model == "R":
self.debug.press_right(wait=True)
self.debug.press_yes()
@ -1890,9 +1732,8 @@ class InputFlowSlip39BasicRecoveryWrongNthWord(InputFlowBase):
self.share = share
self.nth_word = nth_word
def input_flow_tt(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
def input_flow_common(self) -> GeneratorType:
yield from confirm_recovery(self.debug)
# First complete share
yield from slip39_recovery_setup_and_first_share(self.debug, self.share)
@ -1909,39 +1750,8 @@ class InputFlowSlip39BasicRecoveryWrongNthWord(InputFlowBase):
br = yield
assert br.code == messages.ButtonRequestType.Warning
self.client.cancel()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_right()
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input(str(len(self.share)))
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
self.debug.press_yes()
yield # Enter first share
for word in self.share:
self.debug.input(word)
yield # Continue to next share
self.debug.press_yes()
yield # Enter next share
self.debug.press_yes()
yield # Enter next share
for i, word in enumerate(self.share):
if i < self.nth_word:
self.debug.input(word)
else:
self.debug.input(self.share[-1])
break
yield
# assert br.code == messages.ButtonRequestType.Warning
self.client.cancel()
@ -1952,9 +1762,8 @@ class InputFlowSlip39BasicRecoverySameShare(InputFlowBase):
self.first_share = first_share
self.second_share = second_share
def input_flow_tt(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_yes()
def input_flow_common(self) -> GeneratorType:
yield from confirm_recovery(self.debug)
# First complete share
yield from slip39_recovery_setup_and_first_share(self.debug, self.first_share)
@ -1967,44 +1776,9 @@ class InputFlowSlip39BasicRecoverySameShare(InputFlowBase):
br = yield
assert br.code == messages.ButtonRequestType.Warning
# To catch the WARNING screen
self.debug.press_yes()
yield
self.client.cancel()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_right()
self.debug.press_yes()
yield # Homescreen - start process
self.debug.press_yes()
yield # Enter number of words
self.debug.input(str(len(self.first_share)))
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Homescreen - proceed to share entry
self.debug.press_yes()
yield # Enter first share
for word in self.first_share:
self.debug.input(word)
yield # Continue to next share
self.debug.press_yes()
yield # Continue to next share
self.debug.press_yes()
yield # Enter next share
for word in self.second_share:
self.debug.input(word)
br = yield
br = yield
assert br.code == messages.ButtonRequestType.Warning
self.debug.press_right()
self.debug.press_yes()
yield
self.client.cancel()
@ -2012,19 +1786,14 @@ class InputFlowResetSkipBackup(InputFlowBase):
def __init__(self, client: Client):
super().__init__(client)
def input_flow_tt(self) -> GeneratorType:
def input_flow_common(self) -> GeneratorType:
yield # Confirm Recovery
if self.debug.model == "R":
self.debug.press_right()
self.debug.press_yes()
yield # Skip Backup
self.debug.press_no()
yield # Confirm skip backup
self.debug.press_no()
def input_flow_tr(self) -> GeneratorType:
yield # Confirm Recovery
self.debug.press_right()
self.debug.press_yes()
yield # Skip Backup
self.debug.press_no()
yield # Confirm skip backup
if self.debug.model == "R":
self.debug.press_right()
self.debug.press_no()

View File

@ -42,7 +42,7 @@ def test_abort(core_emulator: Emulator):
device_handler.run(device.recover, pin_protection=False)
assert debug.wait_layout().title() == "WALLET RECOVERY"
assert debug.wait_layout().title() == "RECOVER WALLET"
layout = debug.click(buttons.OK, wait=True)
assert "number of words" in layout.text_content()
@ -174,7 +174,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
expected_text = "You have entered"
debug = _restart(device_handler, core_emulator)
assert "You have finished recovering your wallet" in layout.text_content()
assert "Wallet recovered successfully" in layout.text_content()
device_handler = BackgroundDeviceHandler(core_emulator.client)
debug = device_handler.debuglink()

View File

@ -313,7 +313,10 @@ def test_upgrade_shamir_recovery(gen: str, tag: Optional[str]):
recovery.select_number_of_words(debug, wait=not debug.legacy_debug)
layout = recovery.enter_share(debug, MNEMONIC_SLIP39_BASIC_20_3of6[0])
if not debug.legacy_ui and not debug.legacy_debug:
assert "2 more shares" in layout.text_content()
assert (
"1 of 3 shares entered" in layout.text_content()
or "2 more shares" in layout.text_content()
)
device_id = emu.client.features.device_id
storage = emu.get_storage()
@ -327,11 +330,17 @@ def test_upgrade_shamir_recovery(gen: str, tag: Optional[str]):
# second share
layout = recovery.enter_share(debug, MNEMONIC_SLIP39_BASIC_20_3of6[2])
assert "1 more share" in layout.text_content()
assert (
"2 of 3 shares entered" in layout.text_content()
or "1 more share" in layout.text_content()
)
# last one
layout = recovery.enter_share(debug, MNEMONIC_SLIP39_BASIC_20_3of6[1])
assert "You have finished recovering your wallet" in layout.text_content()
assert (
"Wallet recovered successfully" in layout.text_content()
or "finished recovering" in layout.text_content()
)
# Check the result
state = debug.state()