mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
apps/management/recovery_device: mnemonic keyboard
This commit is contained in:
parent
27d9abe883
commit
95db112d10
@ -2,22 +2,41 @@ from trezor import ui, wire
|
||||
from trezor.utils import unimport
|
||||
|
||||
|
||||
def nth(n):
|
||||
if 4 <= n % 100 <= 20:
|
||||
sfx = 'th'
|
||||
else:
|
||||
sfx = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')
|
||||
return str(n) + sfx
|
||||
|
||||
|
||||
@unimport
|
||||
async def layout_recovery_device(ctx, msg):
|
||||
from trezor.crypto import bip39
|
||||
from trezor.messages.FailureType import UnexpectedMessage, ProcessError
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.ui.keyboard import KeyboardMultiTap
|
||||
from trezor.ui.text import Text
|
||||
from apps.common import storage
|
||||
from apps.common.confirm import require_confirm
|
||||
|
||||
msg = 'Please enter ' + nth(msg.word_count) + ' word'
|
||||
if storage.is_initialized():
|
||||
raise wire.FailureError(UnexpectedMessage, 'Already initialized')
|
||||
|
||||
ui.display.clear()
|
||||
ui.header('Recovery device', ui.ICON_RECOVERY, ui.BG, ui.LIGHT_GREEN)
|
||||
ui.display.text(10, 74, msg, ui.BOLD, ui.FG, ui.BG)
|
||||
ui.display.text(10, 104, 'of your mnemonic.', ui.BOLD, ui.FG, ui.BG)
|
||||
words = []
|
||||
|
||||
# TODO
|
||||
kbd = KeyboardMultiTap()
|
||||
for i in range(0, msg.word_count):
|
||||
kbd.prompt = '%s. ' % (i + 1)
|
||||
word = await kbd
|
||||
words.append(word)
|
||||
|
||||
# TODO: confirm words, start again?
|
||||
await require_confirm(ctx, Text(
|
||||
'Recovering seed', ui.ICON_RESET))
|
||||
|
||||
mnemonic = ' '.join(words)
|
||||
|
||||
if not msg.enforce_wordlist and not bip39.check(mnemonic):
|
||||
raise wire.FailureError(ProcessError, 'Mnemonic is not valid')
|
||||
|
||||
# TODO: request pin
|
||||
pin = ''
|
||||
|
||||
storage.load_mnemonic(mnemonic)
|
||||
storage.load_settings(pin=pin,
|
||||
passphrase_protection=msg.passphrase_protection,
|
||||
language=msg.language,
|
||||
label=msg.label)
|
||||
|
@ -4,7 +4,7 @@ from trezor.ui import display
|
||||
from trezor.ui.button import Button, BTN_CLICKED
|
||||
|
||||
|
||||
def cell_area(i, n_x=3, n_y=3, start_x=0, start_y=40, end_x=240, end_y=240 - 48, spacing=0):
|
||||
def cell_area(i, n_x=3, n_y=3, start_x=0, start_y=40, end_x=240, end_y=240, spacing=0):
|
||||
w = (end_x - start_x) // n_x
|
||||
h = (end_y - start_y) // n_y
|
||||
x = (i % n_x) * w
|
||||
@ -32,8 +32,9 @@ def compute_mask(text):
|
||||
|
||||
class KeyboardMultiTap(ui.Widget):
|
||||
|
||||
def __init__(self, content=''):
|
||||
def __init__(self, content='', prompt=''):
|
||||
self.content = content
|
||||
self.prompt = prompt
|
||||
self.sugg_mask = 0xffffffff
|
||||
self.sugg_word = None
|
||||
self.pending_button = None
|
||||
@ -52,8 +53,8 @@ class KeyboardMultiTap(ui.Widget):
|
||||
display.bar(0, 0, 205, 40, ui.BG)
|
||||
|
||||
# input line
|
||||
content_width = display.text_width(self.content, ui.BOLD)
|
||||
display.text(20, 30, self.content, ui.BOLD, ui.FG, ui.BG)
|
||||
content_width = display.text_width(self.prompt + self.content, ui.BOLD)
|
||||
display.text(20, 30, self.prompt + self.content, ui.BOLD, ui.FG, ui.BG)
|
||||
|
||||
# pending marker
|
||||
if self.pending_button is not None:
|
||||
@ -85,13 +86,18 @@ class KeyboardMultiTap(ui.Widget):
|
||||
self._update_suggestion()
|
||||
self._update_buttons()
|
||||
return
|
||||
if self.sugg_button.touch(event, pos) == BTN_CLICKED and self.sugg_word is not None:
|
||||
self.content = self.sugg_word
|
||||
if self.sugg_button.touch(event, pos) == BTN_CLICKED:
|
||||
if self.content == bip39.find_word(self.content):
|
||||
result = self.content
|
||||
self.content = ''
|
||||
elif self.sugg_word is not None:
|
||||
result = None
|
||||
self.content = self.sugg_word
|
||||
self.pending_button = None
|
||||
self.pending_index = 0
|
||||
self._update_suggestion()
|
||||
self._update_buttons()
|
||||
return
|
||||
return result
|
||||
for btn in self.key_buttons:
|
||||
if btn.touch(event, pos) == BTN_CLICKED:
|
||||
if self.pending_button is btn:
|
||||
@ -117,7 +123,7 @@ class KeyboardMultiTap(ui.Widget):
|
||||
|
||||
def _update_buttons(self):
|
||||
for btn in self.key_buttons:
|
||||
if compute_mask(btn.content) & self.sugg_mask:
|
||||
if btn is self.pending_button or compute_mask(btn.content) & self.sugg_mask:
|
||||
btn.enable()
|
||||
else:
|
||||
btn.disable()
|
||||
@ -125,18 +131,27 @@ class KeyboardMultiTap(ui.Widget):
|
||||
def __iter__(self):
|
||||
timeout = loop.sleep(1000 * 1000 * 1)
|
||||
touch = loop.select(io.TOUCH)
|
||||
wait = loop.wait(touch, timeout)
|
||||
while True:
|
||||
wait_timeout = loop.wait(touch, timeout)
|
||||
wait_touch = loop.wait(touch)
|
||||
content = None
|
||||
while content is None:
|
||||
self.render()
|
||||
if self.pending_button is not None:
|
||||
wait = wait_timeout
|
||||
else:
|
||||
wait = wait_touch
|
||||
result = yield wait
|
||||
if touch in wait.finished:
|
||||
event, *pos = result
|
||||
self.touch(event, pos)
|
||||
content = self.touch(event, pos)
|
||||
else:
|
||||
self.pending_button = None
|
||||
self.pending_index = 0
|
||||
self._update_suggestion()
|
||||
self._update_buttons()
|
||||
if self.sugg_word is None:
|
||||
self.content = self.content[:-1]
|
||||
self._update_suggestion()
|
||||
self._update_buttons()
|
||||
return content
|
||||
|
||||
|
||||
def zoom_buttons(keys, upper=False):
|
||||
|
@ -122,12 +122,14 @@ BTN_KEY = {
|
||||
'fg-color': FG,
|
||||
'text-style': MONO,
|
||||
'border-color': BG,
|
||||
'radius': RADIUS,
|
||||
}
|
||||
BTN_KEY_ACTIVE = {
|
||||
'bg-color': GREY,
|
||||
'fg-color': BG,
|
||||
'bg-color': FG,
|
||||
'fg-color': BLACKISH,
|
||||
'text-style': MONO,
|
||||
'border-color': GREY,
|
||||
'border-color': FG,
|
||||
'radius': RADIUS,
|
||||
}
|
||||
|
||||
# loader
|
||||
|
Loading…
Reference in New Issue
Block a user