1
1
import ustruct
2
+ from micropython import const
2
3
from typing import TYPE_CHECKING
3
4
4
5
from storage .cache_common import (
20
21
is_there_a_channel_to_replace ,
21
22
)
22
23
from trezor import protobuf , utils , workflow
24
+ from trezor .loop import Timeout
23
25
24
26
from ..protocol_common import Message
25
27
from . import (
53
55
from .session_context import GenericSessionContext
54
56
55
57
58
+ _MAX_RETRANSMISSION_COUNT = const (50 )
59
+ _MIN_RETRANSMISSION_COUNT = const (2 )
60
+
61
+
56
62
class Reassembler :
57
63
def __init__ (self , cid : int , read_buf : ThpBuffer ) -> None :
58
64
self .cid = cid
@@ -209,7 +215,9 @@ def is_channel_to_replace(self) -> bool:
209
215
# READ and DECRYPT
210
216
211
217
async def recv_payload (
212
- self , expected_ctrl_byte : Callable [[int ], bool ] | None
218
+ self ,
219
+ expected_ctrl_byte : Callable [[int ], bool ] | None ,
220
+ timeout_ms : int | None = None ,
213
221
) -> memoryview :
214
222
"""
215
223
Receive and return a valid THP payload from this channel & its control byte.
@@ -219,10 +227,11 @@ async def recv_payload(
219
227
220
228
If `expected_ctrl_byte` is `None`, returns after the first received ACK.
221
229
"""
230
+
222
231
while True :
223
232
# Handle an existing message (if already reassembled).
224
233
# Otherwise, receive and reassemble a new one.
225
- msg = await self ._get_reassembled_message ()
234
+ msg = await self ._get_reassembled_message (timeout_ms = timeout_ms )
226
235
227
236
# Synchronization process
228
237
ctrl_byte = msg [0 ]
@@ -237,6 +246,8 @@ async def recv_payload(
237
246
continue
238
247
239
248
if expected_ctrl_byte is None or not expected_ctrl_byte (ctrl_byte ):
249
+ if __debug__ :
250
+ self ._log ("Unexpected control byte" , utils .hexlify_if_bytes (msg ))
240
251
raise ThpError ("Unexpected control byte" )
241
252
242
253
# 2: Handle message with unexpected sequential bit
@@ -255,11 +266,13 @@ async def recv_payload(
255
266
256
267
return payload
257
268
258
- async def _get_reassembled_message (self ) -> memoryview :
269
+ async def _get_reassembled_message (
270
+ self , timeout_ms : int | None = None
271
+ ) -> memoryview :
259
272
"""Doesn't block if a message has been already reassembled."""
260
273
while self .reassembler .message is None :
261
274
# receive and reassemble a new message from this channel
262
- channel = await self .ctx .get_next_message ()
275
+ channel = await self .ctx .get_next_message (timeout_ms = timeout_ms )
263
276
if channel is self :
264
277
break
265
278
@@ -377,23 +390,23 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: bytes) -> None:
377
390
# ACK is needed before sending more data
378
391
ABP .set_sending_allowed (self .channel_cache , False )
379
392
380
- # TODO implement retransmissions:
381
- # sender = loop.spawn(self._retransmit(header, payload)) # will raise on timeout
382
- # receiver = loop.spawn(self._wait_for_ack()) # will return on success
383
- # await loop.race(sender, receiver)
384
- await self .ctx .write_payload (header , payload )
385
- await self ._wait_for_ack ()
386
-
387
- # `ABP.set_sending_allowed()` will be called after a valid ACK
388
- assert ABP .is_sending_allowed (self .channel_cache )
389
-
390
- ABP .set_send_seq_bit_to_opposite (self .channel_cache )
393
+ for i in range (_MAX_RETRANSMISSION_COUNT ):
394
+ await self .ctx .write_payload (header , payload )
395
+ # starting from 100ms till ~3.42s
396
+ timeout_ms = round (10200 - 1010000 / (100 + i ))
397
+ try :
398
+ # wait and return after receiving an ACK, or raise in case of an unexpected message.
399
+ await self .recv_payload (expected_ctrl_byte = None , timeout_ms = timeout_ms )
400
+ except Timeout :
401
+ if __debug__ :
402
+ log .warning (__name__ , "Retransmit after %d ms" , timeout_ms )
403
+ continue
404
+ # `ABP.set_sending_allowed()` will be called after a valid ACK
405
+ if ABP .is_sending_allowed (self .channel_cache ):
406
+ ABP .set_send_seq_bit_to_opposite (self .channel_cache )
407
+ return
391
408
392
- async def _wait_for_ack (self ) -> None :
393
- # `ABP.set_sending_allowed()` will be called after a valid ACK
394
- while not ABP .is_sending_allowed (self .channel_cache ):
395
- # wait and return after receiving an ACK, or raise in case of an unexpected message.
396
- await self .recv_payload (expected_ctrl_byte = None )
409
+ raise ThpError ("Retransmission timeout" )
397
410
398
411
def _encrypt (self , buffer : utils .BufferType , noise_payload_len : int ) -> None :
399
412
if __debug__ :
0 commit comments