You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/trezor/ui/layouts/tr/reset.py

298 lines
7.9 KiB

from typing import TYPE_CHECKING
import trezorui2
from trezor import TR
from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled
from ..common import interact
from . import RustLayout, confirm_action, show_warning
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
if TYPE_CHECKING:
from typing import Sequence
from trezor.enums import BackupType
async def show_share_words(
share_words: Sequence[str],
share_index: int | None = None,
group_index: int | None = None,
) -> None:
# Showing words, asking for write down confirmation and preparing for check
br_type = "backup_words"
br_code = ButtonRequestType.ResetDevice
if share_index is None:
title = TR.reset__share_words_title
check_title = TR.reset__check_backup_title
elif group_index is None:
title = f"{TR.words__title_share} #{share_index + 1}"
check_title = (
f"{TR.words__title_check} {TR.words__title_share} #{share_index + 1}"
)
else:
title = f"{TR.words__title_group} {group_index + 1} - {TR.words__title_share} {share_index + 1}"
check_title = f"{TR.words__title_group} {group_index + 1} - {TR.words__title_share} {share_index + 1}"
# We want the option to go back from words to the previous screen
# (by sending CANCELLED)
while True:
await confirm_action(
br_type,
title,
description=TR.reset__write_down_words_template.format(len(share_words)),
verb=TR.buttons__show_words,
verb_cancel=None,
br_code=br_code,
)
result = await interact(
RustLayout(
trezorui2.show_share_words( # type: ignore [Arguments missing for parameters]
share_words=share_words, # type: ignore [No parameter named "share_words"]
)
),
br_type,
br_code,
)
if result is CONFIRMED:
break
await confirm_action(
br_type,
check_title,
description=TR.reset__select_correct_word,
verb=TR.buttons__continue,
verb_cancel=None,
br_code=br_code,
)
async def select_word(
words: Sequence[str],
share_index: int | None,
checked_index: int,
count: int,
group_index: int | None = None,
) -> str:
from trezor.strings import format_ordinal
from trezor.wire.context import wait
# It may happen (with a very low probability)
# that there will be less than three unique words to choose from.
# In that case, duplicating the last word to make it three.
words = list(words)
while len(words) < 3:
words.append(words[-1])
word_ordinal = format_ordinal(checked_index + 1).upper()
result = await wait(
RustLayout(
trezorui2.select_word(
title="",
description=TR.reset__select_word_title_template.format(word_ordinal),
words=(words[0].lower(), words[1].lower(), words[2].lower()),
)
)
)
if __debug__ and isinstance(result, str):
return result
assert isinstance(result, int) and 0 <= result <= 2
return words[result]
async def slip39_show_checklist(step: int, backup_type: BackupType) -> None:
from trezor.enums import BackupType
assert backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced)
items = (
(
TR.reset__slip39_checklist_num_shares,
TR.reset__slip39_checklist_set_threshold,
TR.reset__slip39_checklist_write_down,
)
if backup_type == BackupType.Slip39_Basic
else (
TR.reset__slip39_checklist_num_groups,
TR.reset__slip39_checklist_num_shares,
TR.reset__slip39_checklist_set_sizes,
)
)
result = await interact(
RustLayout(
trezorui2.show_checklist(
title=TR.reset__slip39_checklist_title,
button=TR.buttons__continue,
active=step,
items=items,
)
),
"slip39_checklist",
ButtonRequestType.ResetDevice,
)
if result is not CONFIRMED:
raise ActionCancelled
async def _prompt_number(
title: str,
count: int,
min_count: int,
max_count: int,
br_name: str,
) -> int:
num_input = RustLayout(
trezorui2.request_number(
title=title.upper(),
count=count,
min_count=min_count,
max_count=max_count,
)
)
result = await interact(
num_input,
br_name,
ButtonRequestType.ResetDevice,
)
if __debug__:
if isinstance(result, str):
# debuglink for TR sends a string representing the number
result = CONFIRMED, int(result)
status, value = result
assert status is CONFIRMED
assert isinstance(value, int)
return value
async def slip39_prompt_threshold(
num_of_shares: int, group_id: int | None = None
) -> int:
await confirm_action(
"slip39_prompt_threshold",
TR.words__title_threshold,
description=TR.reset__threshold_info,
verb=TR.buttons__continue,
verb_cancel=None,
)
count = num_of_shares // 2 + 1
# min value of share threshold is 2 unless the number of shares is 1
# number of shares 1 is possible in advanced slip39
min_count = min(2, num_of_shares)
max_count = num_of_shares
if group_id is not None:
title = f"{TR.words__title_threshold} - {TR.words__title_group} {group_id + 1}"
else:
title = TR.words__title_threshold
return await _prompt_number(
title,
count,
min_count,
max_count,
"slip39_threshold",
)
async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int:
await confirm_action(
"slip39_shares",
TR.reset__title_number_of_shares,
description=TR.reset__number_of_shares_info,
verb=TR.buttons__continue,
verb_cancel=None,
)
count = 5
min_count = 1
max_count = 16
if group_id is not None:
title = f"# {TR.words__title_shares} - {TR.words__title_group} {group_id + 1}"
else:
title = TR.reset__title_number_of_shares
return await _prompt_number(
title,
count,
min_count,
max_count,
"slip39_shares",
)
async def slip39_advanced_prompt_number_of_groups() -> int:
count = 5
min_count = 2
max_count = 16
return await _prompt_number(
TR.reset__title_number_of_groups,
count,
min_count,
max_count,
"slip39_groups",
)
async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int:
count = num_of_groups // 2 + 1
min_count = 1
max_count = num_of_groups
return await _prompt_number(
TR.reset__title_group_threshold,
count,
min_count,
max_count,
"slip39_group_threshold",
)
async def show_warning_backup(slip39: bool) -> None:
await show_warning(
"backup_warning",
TR.words__title_remember,
TR.reset__never_make_digital_copy,
TR.buttons__ok_i_understand,
ButtonRequestType.ResetDevice,
)
async def show_success_backup() -> None:
await confirm_action(
"success_backup",
TR.reset__title_backup_is_done,
description=TR.words__keep_it_safe,
verb=TR.buttons__continue,
verb_cancel=None,
br_code=ButtonRequestType.Success,
)
async def show_reset_warning(
br_type: str,
content: str,
subheader: str | None = None,
button: str | None = None,
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> None:
button = button or TR.buttons__try_again # def_arg
await show_warning(
br_type,
subheader or "",
content,
button.upper(),
br_code=br_code,
)