diff --git a/docs/limitations.md b/docs/limitations.md index 2967a050..4f502626 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -127,17 +127,17 @@ We will summarize transaction outputs as "change" back into same wallet, however - key derivatation paths must be 12 or less in depth (`MAX_PATH_DEPTH`) -# NFC Feature (Mk4) +# NFC Feature - can share up to 8000 bytes of PSBT or signed transaction data. - NFC-V (ISO-15693) radio/modulation is common on mobile phones but very rare on desktops -# Fast Wipe (Mk4) +# Fast Wipe - each use of "fast wipe" feature consumes a MCU key slot, of which there are 256. - use _Advanced > Danger Zone > MCU Key Slots_ to view usage -# Trick Pins (Mk4) +# Trick Pins - "deltamode" PIN must be same length as true pin, and differ only in final 4 positions. - there are 14 trick "slots", but we avoid slot 10, so 13 available. @@ -148,9 +148,24 @@ We will summarize transaction outputs as "change" back into same wallet, however is not compatible, the deltamode trick PIN is dropped and not restored - duress wallets are supported when derived from 24- or 12-word seed phrases -# Debug Serial Port (Mk4) +# Debug Serial Port - virtual USB serial port disabled completely by default, and even if enabled in Danger Zone, only echos output, and does not accept any input - use hardware serial port for interactive REPL access (3.3v TTL levels) +# BBQr Scanning (Q) + +- files up to 2MiB in size can be accepted +- when 14 or less parts, displays status of each part, above that, just percent complete + +# QR Scanning (Q) + +- if not BBQr (or sent as unicode inside BBQr) we auto detect these data types: + - PSBT in Base64 or hex + - Wire Transaction in Base64 or hex + - XPRV, XPUB, bare payment addresses, BIP-21 invoices + - seed words (truncated, or full) + - SeedQR (but not Compact SeedQR) +- for Base58, Bech32 encoded values, if checksum is wrong then shown as text + diff --git a/shared/bbqr.py b/shared/bbqr.py index e7903e07..30b740b8 100644 --- a/shared/bbqr.py +++ b/shared/bbqr.py @@ -5,7 +5,7 @@ import utime, uzlib import uasyncio as asyncio from struct import pack, unpack -from utils import B2A +from utils import B2A, problem_file_line from imptask import IMPT from queues import Queue from exceptions import QRDecodeExplained @@ -98,7 +98,10 @@ class BBQrState: # - updates UX to show the progress from glob import dis - hdr = BBQrHeader(scan) + try: + hdr = BBQrHeader(scan) + except Exception as exc: + raise QRDecodeExplained("Bad header: %s" % problem_file_line(exc)) #print("Got %r have %r" % (hdr, self.parts)) @@ -106,6 +109,7 @@ class BBQrState: # New or incompatible header, they might have changed their # minds and are now trying to scan something else; recover self.reset() + self.storage.reset() self.hdr = hdr if hdr.which not in self.parts: @@ -154,6 +158,9 @@ class BBQrStorage: # override this on other projects... which don't have enough ram for whole thing def __init__(self): + self.reset() + + def reset(self): self.buf = None self.hdr = None # could be any header in series self.runt_size = None diff --git a/shared/decoders.py b/shared/decoders.py index 77e09763..41d17649 100644 --- a/shared/decoders.py +++ b/shared/decoders.py @@ -99,7 +99,7 @@ def decode_qr_result(got, expect_secret=False): pass else: - msg = TYPE_LABELS.get(ty, 'Unknown Type') + msg = TYPE_LABELS.get(ty, 'Unknown FileType') raise QRDecodeExplained("Sorry, %s not useful." % msg) # First can we decode a master secret of some type? diff --git a/shared/lcd_display.py b/shared/lcd_display.py index 5a4d4100..e52d5949 100644 --- a/shared/lcd_display.py +++ b/shared/lcd_display.py @@ -594,12 +594,26 @@ class Display: # - lots of data so we can show nice animation # - hdr:BBQrHeader instance count = len(got_parts) - if hdr.num_parts < (CHARS_W // 4): - pat = [('-' if i not in got_parts else str(i+1)) for i in range(hdr.num_parts)] - if corrupt: - pat[hdr.which] = 'X' - pat = (' ' if hdr.num_parts < (CHARS_W//2) else ' ').join(pat) - self.text(None, -3, pat) + if hdr.num_parts < (CHARS_W // 2): + # if not too many parts, show - or 3 as they arrive + pat = [] + for i in range(hdr.num_parts): + if i in got_parts: + pat.append(str(i+1)) + else: + wl = 1 if i < 9 else 2 + if corrupt and i == hdr.which: + pat.append('X'*wl) + else: + pat.append('-'*wl) + + pat = (' ' if hdr.num_parts <= 8 else ' ').join(pat) + if len(pat) > CHARS_W: + pat = '' + else: + pat = '' # clear line + + self.text(None, -3, pat) self.text(None, -2, 'Keep scanning more...' if count < hdr.num_parts else 'Got all parts!') self.text(None, -1, '%s: %d of %d parts' % (hdr.file_label(), count, hdr.num_parts), diff --git a/shared/ux_q1.py b/shared/ux_q1.py index ab8aaa33..1fa29a6c 100644 --- a/shared/ux_q1.py +++ b/shared/ux_q1.py @@ -650,12 +650,12 @@ class QRScannerInteraction: prompt = 'Scan any QR code, or CANCEL' if not expect_secret else \ 'Scan XPRV or Seed Words, or CANCEL' - got = await self.scan(prompt, line2=problem) - if got is None: - return - - # Figure out what we got. try: + got = await self.scan(prompt, line2=problem) + if got is None: + return + + # Figure out what we got. what, vals = decode_qr_result(got, expect_secret=expect_secret) except QRDecodeExplained as exc: problem = str(exc)