apps/management/recovery_device: mnemonic keyboard

This commit is contained in:
Jan Pochyla 2017-11-01 18:17:37 +01:00
parent 27d9abe883
commit 95db112d10
3 changed files with 66 additions and 30 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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