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.
233 lines
6.9 KiB
233 lines
6.9 KiB
from micropython import const
|
|
from typing import TYPE_CHECKING
|
|
|
|
from trezor.enums import ButtonRequestType
|
|
from trezor.ui.layouts import show_success
|
|
from trezor.ui.layouts.reset import ( # noqa: F401
|
|
show_share_words,
|
|
slip39_advanced_prompt_group_threshold,
|
|
slip39_advanced_prompt_number_of_groups,
|
|
slip39_prompt_number_of_shares,
|
|
slip39_prompt_threshold,
|
|
slip39_show_checklist,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from typing import Sequence
|
|
from trezor.wire import GenericContext
|
|
|
|
if __debug__:
|
|
from apps import debug
|
|
|
|
_NUM_OF_CHOICES = const(3)
|
|
|
|
|
|
async def show_internal_entropy(ctx: GenericContext, entropy: bytes) -> None:
|
|
from trezor.ui.layouts import confirm_blob
|
|
|
|
await confirm_blob(
|
|
ctx,
|
|
"entropy",
|
|
"Internal entropy",
|
|
entropy,
|
|
br_code=ButtonRequestType.ResetDevice,
|
|
)
|
|
|
|
|
|
async def _confirm_word(
|
|
ctx: GenericContext,
|
|
share_index: int | None,
|
|
share_words: Sequence[str],
|
|
offset: int,
|
|
count: int,
|
|
group_index: int | None = None,
|
|
) -> bool:
|
|
from trezor.crypto import random
|
|
from trezor.ui.layouts.reset import select_word
|
|
|
|
# remove duplicates
|
|
non_duplicates = list(set(share_words))
|
|
# shuffle list
|
|
random.shuffle(non_duplicates)
|
|
# take top _NUM_OF_CHOICES words
|
|
choices = non_duplicates[:_NUM_OF_CHOICES]
|
|
# select first of them
|
|
checked_word = choices[0]
|
|
# find its index
|
|
checked_index = share_words.index(checked_word) + offset
|
|
# shuffle again so the confirmed word is not always the first choice
|
|
random.shuffle(choices)
|
|
|
|
if __debug__:
|
|
debug.reset_word_index.publish(checked_index)
|
|
|
|
# let the user pick a word
|
|
selected_word: str = await select_word(
|
|
ctx, choices, share_index, checked_index, count, group_index
|
|
)
|
|
# confirm it is the correct one
|
|
return selected_word == checked_word
|
|
|
|
|
|
async def _share_words_confirmed(
|
|
ctx: GenericContext,
|
|
share_index: int | None,
|
|
share_words: Sequence[str],
|
|
num_of_shares: int | None = None,
|
|
group_index: int | None = None,
|
|
) -> bool:
|
|
"""Shows initial dialog asking the user to select words, then presents
|
|
word selectors. Shows success popup if the user is done, failure if the confirmation
|
|
went wrong.
|
|
|
|
Return true if the words are confirmed successfully.
|
|
"""
|
|
# TODO: confirm_action("Select the words bla bla")
|
|
|
|
if await _do_confirm_share_words(ctx, share_index, share_words, group_index):
|
|
await _show_confirmation_success(
|
|
ctx,
|
|
share_index,
|
|
num_of_shares,
|
|
group_index,
|
|
)
|
|
return True
|
|
else:
|
|
await _show_confirmation_failure(ctx)
|
|
|
|
return False
|
|
|
|
|
|
async def _do_confirm_share_words(
|
|
ctx: GenericContext,
|
|
share_index: int | None,
|
|
share_words: Sequence[str],
|
|
group_index: int | None = None,
|
|
) -> bool:
|
|
from trezor import utils
|
|
|
|
# divide list into thirds, rounding up, so that chunking by `third` always yields
|
|
# three parts (the last one might be shorter)
|
|
third = (len(share_words) + 2) // 3
|
|
|
|
offset = 0
|
|
count = len(share_words)
|
|
for part in utils.chunks(share_words, third):
|
|
if not await _confirm_word(ctx, share_index, part, offset, count, group_index):
|
|
return False
|
|
offset += len(part)
|
|
|
|
return True
|
|
|
|
|
|
async def _show_confirmation_success(
|
|
ctx: GenericContext,
|
|
share_index: int | None = None,
|
|
num_of_shares: int | None = None,
|
|
group_index: int | None = None,
|
|
) -> None:
|
|
if share_index is None or num_of_shares is None: # it is a BIP39 backup
|
|
subheader = "You have finished verifying your recovery seed."
|
|
text = ""
|
|
|
|
elif share_index == num_of_shares - 1:
|
|
if group_index is None:
|
|
subheader = "You have finished verifying your recovery shares."
|
|
else:
|
|
subheader = f"You have finished verifying your recovery shares for group {group_index + 1}."
|
|
text = ""
|
|
|
|
else:
|
|
if group_index is None:
|
|
subheader = f"Recovery share #{share_index + 1} checked successfully."
|
|
text = f"Continue with share #{share_index + 2}."
|
|
else:
|
|
subheader = f"Group {group_index + 1} - Share {share_index + 1} checked successfully."
|
|
text = "Continue with the next share."
|
|
|
|
return await show_success(ctx, "success_recovery", text, subheader)
|
|
|
|
|
|
async def _show_confirmation_failure(ctx: GenericContext) -> None:
|
|
from trezor.ui.layouts import show_warning
|
|
|
|
await show_warning(
|
|
ctx,
|
|
"warning_backup_check",
|
|
"Please check again.",
|
|
"That is the wrong word.",
|
|
"Check again",
|
|
ButtonRequestType.ResetDevice,
|
|
)
|
|
|
|
|
|
async def show_backup_warning(ctx: GenericContext, slip39: bool = False) -> None:
|
|
from trezor.ui.layouts.reset import show_warning_backup
|
|
|
|
await show_warning_backup(ctx, slip39)
|
|
|
|
|
|
async def show_backup_success(ctx: GenericContext) -> None:
|
|
text = "Use your backup when you need to recover your wallet."
|
|
await show_success(ctx, "success_backup", text, "Your backup is done.")
|
|
|
|
|
|
# BIP39
|
|
# ===
|
|
|
|
|
|
async def bip39_show_and_confirm_mnemonic(ctx: GenericContext, mnemonic: str) -> None:
|
|
# warn user about mnemonic safety
|
|
await show_backup_warning(ctx)
|
|
|
|
words = mnemonic.split()
|
|
|
|
while True:
|
|
# display paginated mnemonic on the screen
|
|
await show_share_words(ctx, words)
|
|
|
|
# make the user confirm some words from the mnemonic
|
|
if await _share_words_confirmed(ctx, None, words):
|
|
break # this share is confirmed, go to next one
|
|
|
|
|
|
# SLIP39
|
|
# ===
|
|
|
|
|
|
async def slip39_basic_show_and_confirm_shares(
|
|
ctx: GenericContext, shares: Sequence[str]
|
|
) -> None:
|
|
# warn user about mnemonic safety
|
|
await show_backup_warning(ctx, True)
|
|
|
|
for index, share in enumerate(shares):
|
|
share_words = share.split(" ")
|
|
while True:
|
|
# display paginated share on the screen
|
|
await show_share_words(ctx, share_words, index)
|
|
|
|
# make the user confirm words from the share
|
|
if await _share_words_confirmed(ctx, index, share_words, len(shares)):
|
|
break # this share is confirmed, go to next one
|
|
|
|
|
|
async def slip39_advanced_show_and_confirm_shares(
|
|
ctx: GenericContext, shares: Sequence[Sequence[str]]
|
|
) -> None:
|
|
# warn user about mnemonic safety
|
|
await show_backup_warning(ctx, True)
|
|
|
|
for group_index, group in enumerate(shares):
|
|
for share_index, share in enumerate(group):
|
|
share_words = share.split(" ")
|
|
while True:
|
|
# display paginated share on the screen
|
|
await show_share_words(ctx, share_words, share_index, group_index)
|
|
|
|
# make the user confirm words from the share
|
|
if await _share_words_confirmed(
|
|
ctx, share_index, share_words, len(group), group_index
|
|
):
|
|
break # this share is confirmed, go to next one
|