mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-03 03:50:58 +00:00
refactor(core/wire): move restarting control to handle_single_message
that way it is possible to avoid restarting if we failed to find a handler for a message (makes it faster with no ill effects because "failed to find handler" (a) shouldn't happen and (b) doesn't cause any significant fragmentation)
This commit is contained in:
parent
8870869f93
commit
cab6fd0799
@ -89,20 +89,19 @@ if __debug__:
|
|||||||
|
|
||||||
async def _handle_single_message(
|
async def _handle_single_message(
|
||||||
ctx: context.Context, msg: codec_v1.Message, use_workflow: bool
|
ctx: context.Context, msg: codec_v1.Message, use_workflow: bool
|
||||||
) -> codec_v1.Message | None:
|
) -> bool:
|
||||||
"""Handle a message that was loaded from USB by the caller.
|
"""Handle a message that was loaded from USB by the caller.
|
||||||
|
|
||||||
Find the appropriate handler, run it and write its result on the wire. In case
|
Find the appropriate handler, run it and write its result on the wire. In case
|
||||||
a problem is encountered at any point, write the appropriate error on the wire.
|
a problem is encountered at any point, write the appropriate error on the wire.
|
||||||
|
|
||||||
If the workflow finished normally or with an error, the return value is None.
|
The return value indicates whether to override the default restarting behavior. If
|
||||||
|
`False` is returned, the caller is allowed to clear the loop and restart the
|
||||||
If an unexpected message had arrived on the wire while the workflow was processing,
|
MicroPython machine (see `session.py`). This would lose all state and incurs a cost
|
||||||
the workflow is shut down with an `UnexpectedMessage` exception. This is not
|
in terms of repeated startup time. When handling the message didn't cause any
|
||||||
considered an "error condition" to return over the wire -- instead the message
|
significant fragmentation (e.g., if decoding the message was skipped), or if
|
||||||
is processed as if starting a new workflow.
|
the type of message is supposed to be optimized and not disrupt the running state,
|
||||||
In such case, the `UnexpectedMessage` is caught and the message is returned
|
this function will return `True`.
|
||||||
to the caller. It will then be processed in the next iteration of the message loop.
|
|
||||||
"""
|
"""
|
||||||
if __debug__:
|
if __debug__:
|
||||||
try:
|
try:
|
||||||
@ -126,7 +125,7 @@ async def _handle_single_message(
|
|||||||
# Handlers are allowed to exception out. In that case, we can skip decoding
|
# Handlers are allowed to exception out. In that case, we can skip decoding
|
||||||
# and return the error.
|
# and return the error.
|
||||||
await ctx.write(failure(exc))
|
await ctx.write(failure(exc))
|
||||||
return None
|
return True
|
||||||
|
|
||||||
if msg.type in workflow.ALLOW_WHILE_LOCKED:
|
if msg.type in workflow.ALLOW_WHILE_LOCKED:
|
||||||
workflow.autolock_interrupts_workflow = False
|
workflow.autolock_interrupts_workflow = False
|
||||||
@ -158,16 +157,21 @@ async def _handle_single_message(
|
|||||||
# results of the handler.
|
# results of the handler.
|
||||||
res_msg = await task
|
res_msg = await task
|
||||||
|
|
||||||
except context.UnexpectedMessage as exc:
|
except context.UnexpectedMessage:
|
||||||
# Workflow was trying to read a message from the wire, and
|
# Workflow was trying to read a message from the wire, and
|
||||||
# something unexpected came in. See Context.read() for
|
# something unexpected came in. See Context.read() for
|
||||||
# example, which expects some particular message and raises
|
# example, which expects some particular message and raises
|
||||||
# UnexpectedMessage if another one comes in.
|
# UnexpectedMessage if another one comes in.
|
||||||
# In order not to lose the message, we return it to the caller.
|
#
|
||||||
# TODO:
|
# We process the unexpected message by aborting the current workflow and
|
||||||
# We might handle only the few common cases here, like
|
# possibly starting a new one, initiated by that message. (The main usecase
|
||||||
# Initialize and Cancel.
|
# being, the host does not finish the workflow, we want other callers to
|
||||||
return exc.msg
|
# be able to do their own thing.)
|
||||||
|
#
|
||||||
|
# The message is stored in the exception, which we re-raise for the caller
|
||||||
|
# to process. It is not a standard exception that should be logged and a result
|
||||||
|
# sent to the wire.
|
||||||
|
raise
|
||||||
|
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
# Either:
|
# Either:
|
||||||
@ -189,7 +193,9 @@ async def _handle_single_message(
|
|||||||
# perform the write outside the big try-except block, so that usb write
|
# perform the write outside the big try-except block, so that usb write
|
||||||
# problem bubbles up
|
# problem bubbles up
|
||||||
await ctx.write(res_msg)
|
await ctx.write(res_msg)
|
||||||
return None
|
|
||||||
|
# Look into `AVOID_RESTARTING_FOR` to see if this message should avoid restarting.
|
||||||
|
return msg.type in AVOID_RESTARTING_FOR
|
||||||
|
|
||||||
|
|
||||||
async def handle_session(
|
async def handle_session(
|
||||||
@ -230,9 +236,16 @@ async def handle_session(
|
|||||||
next_msg = None
|
next_msg = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
next_msg = await _handle_single_message(
|
do_not_restart = await _handle_single_message(
|
||||||
ctx, msg, use_workflow=not is_debug_session
|
ctx, msg, use_workflow=not is_debug_session
|
||||||
)
|
)
|
||||||
|
except context.UnexpectedMessage as unexpected:
|
||||||
|
# The workflow was interrupted by an unexpected message. We need to
|
||||||
|
# process it as if it was a new message...
|
||||||
|
next_msg = unexpected.msg
|
||||||
|
# ...and we must not restart because that would lose the message.
|
||||||
|
do_not_restart = True
|
||||||
|
continue
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# Log and ignore. The session handler can only exit explicitly in the
|
# Log and ignore. The session handler can only exit explicitly in the
|
||||||
# following finally block.
|
# following finally block.
|
||||||
@ -246,8 +259,7 @@ async def handle_session(
|
|||||||
# workflow running on wire.
|
# workflow running on wire.
|
||||||
utils.unimport_end(modules)
|
utils.unimport_end(modules)
|
||||||
|
|
||||||
if next_msg is None and msg.type not in AVOID_RESTARTING_FOR:
|
if not do_not_restart:
|
||||||
# Shut down the loop if there is no next message waiting.
|
|
||||||
# Let the session be restarted from `main`.
|
# Let the session be restarted from `main`.
|
||||||
loop.clear()
|
loop.clear()
|
||||||
return # pylint: disable=lost-exception
|
return # pylint: disable=lost-exception
|
||||||
|
Loading…
Reference in New Issue
Block a user