core: use channels to give feedback over debuglink

all debug input signals are now channels, and DebugLinkDecision handler
waits until the input was consumed. This means that the input events are
queued; originally, if an input event arrived before the previous was
consumed, the previous input would be lost.

reset words and their positions are now also channels, and
DebugLinkGetState can wait for their updates, if required
pull/328/head
matejcik 5 years ago committed by matejcik
parent 6e7fc5f601
commit 0890f68c0c

@ -24,6 +24,8 @@ message DebugLinkDecision {
* @next DebugLinkState * @next DebugLinkState
*/ */
message DebugLinkGetState { message DebugLinkGetState {
optional bool wait_word_list = 1; // Trezor T only - wait until mnemonic words are shown
optional bool wait_word_pos = 2; // Trezor T only - wait until reset word position is requested
} }
/** /**

@ -4,36 +4,60 @@ if not __debug__:
halt("debug mode inactive") halt("debug mode inactive")
if __debug__: if __debug__:
from trezor import config, loop, utils from trezor import config, log, loop, utils
from trezor.messages import MessageType from trezor.messages import MessageType
from trezor.wire import register, protobuf_workflow from trezor.wire import register, protobuf_workflow
if False: if False:
from typing import List, Optional from typing import Optional
from trezor import wire from trezor import wire
from trezor.messages.DebugLinkDecision import DebugLinkDecision from trezor.messages.DebugLinkDecision import DebugLinkDecision
from trezor.messages.DebugLinkGetState import DebugLinkGetState from trezor.messages.DebugLinkGetState import DebugLinkGetState
from trezor.messages.DebugLinkState import DebugLinkState from trezor.messages.DebugLinkState import DebugLinkState
reset_internal_entropy = None # type: Optional[bytes] reset_internal_entropy = None # type: Optional[bytes]
reset_current_words = None # type: Optional[List[str]] reset_current_words = loop.chan()
reset_word_index = None # type: Optional[int] reset_word_index = loop.chan()
confirm_signal = loop.signal() confirm_chan = loop.chan()
swipe_signal = loop.signal() swipe_chan = loop.chan()
input_signal = loop.signal() input_chan = loop.chan()
confirm_signal = confirm_chan.take
swipe_signal = swipe_chan.take
input_signal = input_chan.take
async def dispatch_DebugLinkDecision( async def dispatch_DebugLinkDecision(
ctx: wire.Context, msg: DebugLinkDecision ctx: wire.Context, msg: DebugLinkDecision
) -> None: ) -> None:
from trezor.ui import confirm, swipe from trezor.ui import confirm, swipe
waiting_signals = [
bool(s.putters) for s in (confirm_chan, swipe_chan, input_chan)
]
if sum(waiting_signals) > 0:
log.warning(
__name__,
"Received new DebugLinkDecision before the previous one was handled.",
)
log.warning(
__name__,
"received: button {}, swipe {}, input {}".format(
msg.yes_no, msg.up_down, msg.input
),
)
log.warning(
__name__,
"waiting: button {}, swipe {}, input {}".format(*waiting_signals),
)
if msg.yes_no is not None: if msg.yes_no is not None:
confirm_signal.send(confirm.CONFIRMED if msg.yes_no else confirm.CANCELLED) await confirm_chan.put(
confirm.CONFIRMED if msg.yes_no else confirm.CANCELLED
)
if msg.up_down is not None: if msg.up_down is not None:
swipe_signal.send(swipe.SWIPE_DOWN if msg.up_down else swipe.SWIPE_UP) await swipe_chan.put(swipe.SWIPE_DOWN if msg.up_down else swipe.SWIPE_UP)
if msg.input is not None: if msg.input is not None:
input_signal.send(msg.input) await input_chan.put(msg.input)
async def dispatch_DebugLinkGetState( async def dispatch_DebugLinkGetState(
ctx: wire.Context, msg: DebugLinkGetState ctx: wire.Context, msg: DebugLinkGetState
@ -45,10 +69,12 @@ if __debug__:
m.mnemonic_secret = mnemonic.get_secret() m.mnemonic_secret = mnemonic.get_secret()
m.mnemonic_type = mnemonic.get_type() m.mnemonic_type = mnemonic.get_type()
m.passphrase_protection = storage.device.has_passphrase() m.passphrase_protection = storage.device.has_passphrase()
m.reset_word_pos = reset_word_index
m.reset_entropy = reset_internal_entropy m.reset_entropy = reset_internal_entropy
if reset_current_words:
m.reset_word = " ".join(reset_current_words) if msg.wait_word_pos:
m.reset_word_pos = await reset_word_index.take()
if msg.wait_word_list:
m.reset_word = " ".join(await reset_current_words.take())
return m return m
def boot() -> None: def boot() -> None:

@ -93,7 +93,7 @@ async def _confirm_word(ctx, share_index, numbered_share_words, count):
# we always confirm the first (random) word index # we always confirm the first (random) word index
checked_index, checked_word = numbered_choices[0] checked_index, checked_word = numbered_choices[0]
if __debug__: if __debug__:
debug.reset_word_index = checked_index debug.reset_word_index.publish(checked_index)
# shuffle again so the confirmed word is not always the first choice # shuffle again so the confirmed word is not always the first choice
random.shuffle(numbered_choices) random.shuffle(numbered_choices)
@ -200,7 +200,7 @@ async def _bip39_show_mnemonic(ctx, words: list):
def export_displayed_words(): def export_displayed_words():
# export currently displayed mnemonic words into debuglink # export currently displayed mnemonic words into debuglink
debug.reset_current_words = [w for _, w in words[paginated.page]] debug.reset_current_words.publish([w for _, w in words[paginated.page]])
paginated.on_change = export_displayed_words paginated.on_change = export_displayed_words
export_displayed_words() export_displayed_words()
@ -393,7 +393,8 @@ async def _slip39_show_share_words(ctx, share_index, share_words):
def export_displayed_words(): def export_displayed_words():
# export currently displayed mnemonic words into debuglink # export currently displayed mnemonic words into debuglink
debug.reset_current_words = [w for _, w in word_pages[paginated.page]] words = [w for _, w in word_pages[paginated.page]]
debug.reset_current_words.publish(words)
paginated.on_change = export_displayed_words paginated.on_change = export_displayed_words
export_displayed_words() export_displayed_words()

@ -12,3 +12,18 @@ if __debug__:
class DebugLinkGetState(p.MessageType): class DebugLinkGetState(p.MessageType):
MESSAGE_WIRE_TYPE = 101 MESSAGE_WIRE_TYPE = 101
def __init__(
self,
wait_word_list: bool = None,
wait_word_pos: bool = None,
) -> None:
self.wait_word_list = wait_word_list
self.wait_word_pos = wait_word_pos
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('wait_word_list', p.BoolType, 0),
2: ('wait_word_pos', p.BoolType, 0),
}

@ -12,3 +12,18 @@ if __debug__:
class DebugLinkGetState(p.MessageType): class DebugLinkGetState(p.MessageType):
MESSAGE_WIRE_TYPE = 101 MESSAGE_WIRE_TYPE = 101
def __init__(
self,
wait_word_list: bool = None,
wait_word_pos: bool = None,
) -> None:
self.wait_word_list = wait_word_list
self.wait_word_pos = wait_word_pos
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('wait_word_list', p.BoolType, 0),
2: ('wait_word_pos', p.BoolType, 0),
}

Loading…
Cancel
Save