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
|
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
|
@unimport
|
||||||
async def layout_recovery_device(ctx, msg):
|
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()
|
words = []
|
||||||
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)
|
|
||||||
|
|
||||||
# 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
|
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
|
w = (end_x - start_x) // n_x
|
||||||
h = (end_y - start_y) // n_y
|
h = (end_y - start_y) // n_y
|
||||||
x = (i % n_x) * w
|
x = (i % n_x) * w
|
||||||
@ -32,8 +32,9 @@ def compute_mask(text):
|
|||||||
|
|
||||||
class KeyboardMultiTap(ui.Widget):
|
class KeyboardMultiTap(ui.Widget):
|
||||||
|
|
||||||
def __init__(self, content=''):
|
def __init__(self, content='', prompt=''):
|
||||||
self.content = content
|
self.content = content
|
||||||
|
self.prompt = prompt
|
||||||
self.sugg_mask = 0xffffffff
|
self.sugg_mask = 0xffffffff
|
||||||
self.sugg_word = None
|
self.sugg_word = None
|
||||||
self.pending_button = None
|
self.pending_button = None
|
||||||
@ -52,8 +53,8 @@ class KeyboardMultiTap(ui.Widget):
|
|||||||
display.bar(0, 0, 205, 40, ui.BG)
|
display.bar(0, 0, 205, 40, ui.BG)
|
||||||
|
|
||||||
# input line
|
# input line
|
||||||
content_width = display.text_width(self.content, ui.BOLD)
|
content_width = display.text_width(self.prompt + self.content, ui.BOLD)
|
||||||
display.text(20, 30, self.content, ui.BOLD, ui.FG, ui.BG)
|
display.text(20, 30, self.prompt + self.content, ui.BOLD, ui.FG, ui.BG)
|
||||||
|
|
||||||
# pending marker
|
# pending marker
|
||||||
if self.pending_button is not None:
|
if self.pending_button is not None:
|
||||||
@ -85,13 +86,18 @@ class KeyboardMultiTap(ui.Widget):
|
|||||||
self._update_suggestion()
|
self._update_suggestion()
|
||||||
self._update_buttons()
|
self._update_buttons()
|
||||||
return
|
return
|
||||||
if self.sugg_button.touch(event, pos) == BTN_CLICKED and self.sugg_word is not None:
|
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.content = self.sugg_word
|
||||||
self.pending_button = None
|
self.pending_button = None
|
||||||
self.pending_index = 0
|
self.pending_index = 0
|
||||||
self._update_suggestion()
|
self._update_suggestion()
|
||||||
self._update_buttons()
|
self._update_buttons()
|
||||||
return
|
return result
|
||||||
for btn in self.key_buttons:
|
for btn in self.key_buttons:
|
||||||
if btn.touch(event, pos) == BTN_CLICKED:
|
if btn.touch(event, pos) == BTN_CLICKED:
|
||||||
if self.pending_button is btn:
|
if self.pending_button is btn:
|
||||||
@ -117,7 +123,7 @@ class KeyboardMultiTap(ui.Widget):
|
|||||||
|
|
||||||
def _update_buttons(self):
|
def _update_buttons(self):
|
||||||
for btn in self.key_buttons:
|
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()
|
btn.enable()
|
||||||
else:
|
else:
|
||||||
btn.disable()
|
btn.disable()
|
||||||
@ -125,18 +131,27 @@ class KeyboardMultiTap(ui.Widget):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
timeout = loop.sleep(1000 * 1000 * 1)
|
timeout = loop.sleep(1000 * 1000 * 1)
|
||||||
touch = loop.select(io.TOUCH)
|
touch = loop.select(io.TOUCH)
|
||||||
wait = loop.wait(touch, timeout)
|
wait_timeout = loop.wait(touch, timeout)
|
||||||
while True:
|
wait_touch = loop.wait(touch)
|
||||||
|
content = None
|
||||||
|
while content is None:
|
||||||
self.render()
|
self.render()
|
||||||
|
if self.pending_button is not None:
|
||||||
|
wait = wait_timeout
|
||||||
|
else:
|
||||||
|
wait = wait_touch
|
||||||
result = yield wait
|
result = yield wait
|
||||||
if touch in wait.finished:
|
if touch in wait.finished:
|
||||||
event, *pos = result
|
event, *pos = result
|
||||||
self.touch(event, pos)
|
content = self.touch(event, pos)
|
||||||
else:
|
else:
|
||||||
self.pending_button = None
|
self.pending_button = None
|
||||||
self.pending_index = 0
|
self.pending_index = 0
|
||||||
|
if self.sugg_word is None:
|
||||||
|
self.content = self.content[:-1]
|
||||||
self._update_suggestion()
|
self._update_suggestion()
|
||||||
self._update_buttons()
|
self._update_buttons()
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
def zoom_buttons(keys, upper=False):
|
def zoom_buttons(keys, upper=False):
|
||||||
|
@ -122,12 +122,14 @@ BTN_KEY = {
|
|||||||
'fg-color': FG,
|
'fg-color': FG,
|
||||||
'text-style': MONO,
|
'text-style': MONO,
|
||||||
'border-color': BG,
|
'border-color': BG,
|
||||||
|
'radius': RADIUS,
|
||||||
}
|
}
|
||||||
BTN_KEY_ACTIVE = {
|
BTN_KEY_ACTIVE = {
|
||||||
'bg-color': GREY,
|
'bg-color': FG,
|
||||||
'fg-color': BG,
|
'fg-color': BLACKISH,
|
||||||
'text-style': MONO,
|
'text-style': MONO,
|
||||||
'border-color': GREY,
|
'border-color': FG,
|
||||||
|
'radius': RADIUS,
|
||||||
}
|
}
|
||||||
|
|
||||||
# loader
|
# loader
|
||||||
|
Loading…
Reference in New Issue
Block a user