async def failure(self, msg, exc=None, title='Failure'): self.failed = msg self.done() # show line number and/or simple text about error if exc: print("%s:" % msg) sys.print_exception(exc) msg += '\n\n' em = str(exc) if em: msg += em msg += '\n\n' msg += problem_file_line(exc) from glob import hsm_active, dis # do nothing more for HSM case: msg will be available over USB if hsm_active: dis.progress_bar(1) # finish the Validating... or whatever was up return # may be a user-abort waiting, but we want to see error msg; so clear it ux_clear_keys(True) return await ux_show_story(msg, title)
async def interact(self): # Just show the address... no real confirmation needed. from glob import hsm_active, dis if not hsm_active: msg = self.get_msg() msg += '\n\nCompare this payment address to the one shown on your other, less-trusted, software.' if version.has_fatram: msg += ' Press 4 to view QR Code.' while 1: ch = await ux_show_story(msg, title=self.title, escape='4') if ch == '4' and version.has_fatram: q = ux.QRDisplay([self.address], (self.addr_fmt & AFC_BECH32)) await q.interact_bare() continue break else: # finish the Wait... dis.progress_bar(1) self.done() UserAuthorizedAction.cleanup() # because no results to store
async def test_sflash(): dis.clear() dis.text(None, 18, 'Serial Flash') dis.show() from sflash import SF from ustruct import pack import ngu msize = 1024 * 1024 SF.chip_erase() for phase in [0, 1]: steps = 7 * 4 for i in range(steps): dis.progress_bar(i / steps) dis.show() await sleep_ms(250) if not SF.is_busy(): break assert not SF.is_busy() # "didn't finish" # leave chip blank if phase == 1: break buf = bytearray(32) for addr in range(0, msize, 1024): SF.read(addr, buf) assert set(buf) == {255} # "not blank" rnd = ngu.hash.sha256s(pack('I', addr)) SF.write(addr, rnd) SF.read(addr, buf) assert buf == rnd # "write failed" dis.progress_bar_show(addr / msize) # check no aliasing, also right size part for addr in range(0, msize, 1024): expect = ngu.hash.sha256s(pack('I', addr)) SF.read(addr, buf) assert buf == expect # "readback failed" dis.progress_bar_show(addr / msize)
async def interact(self): # Prompt user w/ details and get approval from glob import dis, hsm_active # step 1: parse PSBT from sflash into in-memory objects. try: dis.fullscreen("Reading...") with SFFile(TXN_INPUT_OFFSET, length=self.psbt_len) as fd: self.psbt = psbtObject.read_psbt(fd) except BaseException as exc: if isinstance(exc, MemoryError): msg = "Transaction is too complex" exc = None else: msg = "PSBT parse failed" return await self.failure(msg, exc) dis.fullscreen("Validating...") # Do some analysis/ validation try: await self.psbt.validate() # might do UX: accept multisig import self.psbt.consider_inputs() self.psbt.consider_keys() self.psbt.consider_outputs() except FraudulentChangeOutput as exc: print('FraudulentChangeOutput: ' + exc.args[0]) return await self.failure(exc.args[0], title='Change Fraud') except FatalPSBTIssue as exc: print('FatalPSBTIssue: ' + exc.args[0]) return await self.failure(exc.args[0]) except BaseException as exc: del self.psbt gc.collect() if isinstance(exc, MemoryError): msg = "Transaction is too complex" exc = None else: msg = "Invalid PSBT" return await self.failure(msg, exc) # step 2: figure out what we are approving, so we can get sign-off # - outputs, amounts # - fee # # notes: # - try to handle lots of outputs # - cannot calc fee as sat/byte, only as percent # - somethings are 'warnings': # - fee too big # - inputs we can't sign (no key) # try: msg = uio.StringIO() # mention warning at top wl= len(self.psbt.warnings) if wl == 1: msg.write('(1 warning below)\n\n') elif wl >= 2: msg.write('(%d warnings below)\n\n' % wl) self.output_summary_text(msg) gc.collect() fee = self.psbt.calculate_fee() if fee is not None: msg.write("\nNetwork fee:\n%s %s\n" % self.chain.render_value(fee)) # NEW: show where all the change outputs are going self.output_change_text(msg) gc.collect() if self.psbt.warnings: msg.write('\n---WARNING---\n\n') for label, m in self.psbt.warnings: msg.write('- %s: %s\n\n' % (label, m)) if self.do_visualize: # stop here and just return the text of approval message itself self.result = await self.save_visualization(msg, (self.stxn_flags & STXN_SIGNED)) del self.psbt self.done() return if not hsm_active: msg.write("\nPress OK to approve and sign transaction. X to abort.") ch = await ux_show_story(msg, title="OK TO SEND?") else: ch = await hsm_active.approve_transaction(self.psbt, self.psbt_sha, msg.getvalue()) dis.progress_bar(1) # finish the Validating... except MemoryError: # recovery? maybe. try: del self.psbt del msg except: pass # might be NameError since we don't know how far we got gc.collect() msg = "Transaction is too complex" return await self.failure(msg) if ch != 'y': # they don't want to! self.refused = True await ux_dramatic_pause("Refused.", 1) del self.psbt self.done() return # do the actual signing. try: dis.fullscreen('Wait...') gc.collect() # visible delay causes by this but also sign_it() below self.psbt.sign_it() except FraudulentChangeOutput as exc: return await self.failure(exc.args[0], title='Change Fraud') except MemoryError: msg = "Transaction is too complex" return await self.failure(msg) except BaseException as exc: return await self.failure("Signing failed late", exc) if self.approved_cb: # for micro sd case await self.approved_cb(self.psbt) self.done() return txid = None try: # re-serialize the PSBT back out with SFFile(TXN_OUTPUT_OFFSET, max_size=MAX_TXN_LEN, message="Saving...") as fd: await fd.erase() if self.do_finalize: txid = self.psbt.finalize(fd) else: self.psbt.serialize(fd) self.result = (fd.tell(), fd.checksum.digest()) self.done(redraw=(not txid)) except BaseException as exc: return await self.failure("PSBT output failed", exc) if self.do_finalize and txid and not hsm_active: # Show txid when we can; advisory # - maybe even as QR, hex-encoded in alnum mode tmsg = txid if version.has_fatram: tmsg += '\n\nPress 1 for QR Code of TXID.' ch = await ux_show_story(tmsg, "Final TXID", escape='1') if version.has_fatram and ch=='1': await show_qr_code(txid, True)