async def wipe(self): try: delete_recursively(fpath("/flash")) delete_recursively(fpath("/qspi")) await self.gui.alert("Success!", "All the content is deleted.") except Exception as e: await self.gui.alert("Fail!", "Something bad happened:\n"+str(e))
def get_keystore(self): """Clean up the test folder and create fresh keystore""" try: platform.delete_recursively(TEST_DIR) os.rmdir(TEST_DIR) except: pass return FlashKeyStore(TEST_DIR)
async def get_data(self): delete_recursively(self.path) if self.manager is not None: # pass self so user can abort await self.manager.gui.show_progress( self, "Scanning...", "Point scanner to the QR code") stream = await self.scan() if stream is not None: return stream
def load_wallet(self, path): """Loads a wallet with particular id""" try: # pass path and key for verification return self.WalletClass.from_path(path, self.keystore) except Exception as e: # if we failed to load -> delete folder and throw an error platform.delete_recursively(path, include_self=True) raise e
def load_wallet(self, path): """Loads a wallet with particular id""" w = None # going through all wallet classes and trying to load # first we verify descriptor sig in the folder for walletcls in self.WALLETS: try: # pass path and key for verification w = walletcls.from_path(path, self.keystore) # if fails - we continue, otherwise - we are done break except Exception as e: pass # if we failed to load -> delete folder and throw an error if w is None: platform.delete_recursively(path, include_self=True) raise WalletError("Can't load wallet from %s" % path) return w
def wipe(self): """Deletes all wallets info""" self.wallets = [] self.path = None platform.delete_recursively(self.root_path)
def cleanup(self): if self.f is not None: self.f.close() self.f = None platform.delete_recursively(self.path)
def wipe(self, path): """Delete everything in path""" platform.delete_recursively(path)
def wipe(self): if self.path is None: raise WalletError("I don't know path...") delete_recursively(self.path, include_self=True)
def wipe(self): # TODO: wipe the smartcard as well? delete_recursively(fpath("/flash")) delete_recursively(fpath("/qspi"))
async def process_host_command(self, stream, show_screen): platform.delete_recursively(self.tempdir) cmd, stream = self.parse_stream(stream) if cmd == SIGN_PSBT: encoding = BASE64_STREAM if stream.read(5) == b"psbt\xff": encoding = RAW_STREAM stream.seek(-5, 1) res = await self.sign_psbt(stream, show_screen, encoding) if res is not None: obj = { "title": "Transaction is signed!", "message": "Scan it with your wallet", } return res, obj return if cmd == SIGN_BCUR: # move to the end of UR:BYTES/ stream.seek(9, 1) # move to the end of hash if it's there d = stream.read(70) if b"/" in d: pos = d.index(b"/") stream.seek(pos - len(d) + 1, 1) else: stream.seek(-len(d), 1) with open(self.tempdir + "/raw", "wb") as f: bcur_decode_stream(stream, f) gc.collect() with open(self.tempdir + "/raw", "rb") as f: res = await self.sign_psbt(f, show_screen, encoding=RAW_STREAM) platform.delete_recursively(self.tempdir) if res is not None: data, hsh = bcur_encode(res.read(), upper=True) bcur_res = (b"UR:BYTES/" + hsh + "/" + data) obj = { "title": "Transaction is signed!", "message": "Scan it with your wallet", } gc.collect() return BytesIO(bcur_res), obj return elif cmd == ADD_WALLET: # read content, it's small desc = stream.read().decode().strip() w = self.parse_wallet(desc) res = await self.confirm_new_wallet(w, show_screen) if res: self.add_wallet(w) return elif cmd == VERIFY_ADDRESS: data = stream.read().decode().replace("bitcoin:", "") # should be of the form addr?index=N or similar if "index=" not in data or "?" not in data: raise WalletError("Can't verify address with unknown index") addr, rest = data.split("?") args = rest.split("&") idx = None for arg in args: if arg.startswith("index="): idx = int(arg[6:]) break w, _ = self.find_wallet_from_address(addr, index=idx) await show_screen(WalletScreen(w, self.network, idx)) return elif cmd == DERIVE_ADDRESS: arr = stream.read().split(b" ") redeem_script = None if len(arr) == 2: script_type, path = arr elif len(arr) == 3: script_type, path, redeem_script = arr else: raise WalletError("Too many arguments") paths = [p.decode() for p in path.split(b",")] if len(paths) == 0: raise WalletError("Invalid path argument") res = await self.showaddr(paths, script_type, redeem_script, show_screen=show_screen) return BytesIO(res), {} else: raise WalletError("Unknown command")
async def process_host_command(self, stream, show_screen): platform.delete_recursively(self.tempdir) cmd, stream = self.parse_stream(stream) if cmd == SIGN_PSBT: magic = stream.read(len(self.PSBTViewClass.MAGIC)) if magic == self.PSBTViewClass.MAGIC: encoding = RAW_STREAM elif magic.startswith(self.B64PSBT_PREFIX): encoding = BASE64_STREAM else: raise WalletError("Invalid PSBT magic!") stream.seek(-len(magic), 1) res = await self.sign_psbt(stream, show_screen, encoding) if res is not None: obj = { "title": "Transaction is signed!", "message": "Scan it with your wallet", } return res, obj return if cmd == SIGN_BCUR: # move to the end of UR:BYTES/ stream.seek(9, 1) # move to the end of hash if it's there d = stream.read(70) if b"/" in d: pos = d.index(b"/") stream.seek(pos-len(d)+1, 1) else: stream.seek(-len(d), 1) with open(self.tempdir+"/raw", "wb") as f: bcur_decode_stream(stream, f) gc.collect() with open(self.tempdir+"/raw", "rb") as f: res = await self.sign_psbt(f, show_screen, encoding=RAW_STREAM) if res is not None: # bcur-encode to temp data file with open(self.tempdir+"/bcur_data", "wb") as fout: if isinstance(res, str): with open(res, "rb") as fin: l, hsh = bcur_encode_stream(fin, fout, upper=True) else: l, hsh = bcur_encode_stream(res, fout, upper=True) # add prefix and hash with open(self.tempdir+"/bcur_full", "wb") as fout: fout.write(b"UR:BYTES/") fout.write(hsh) fout.write(b"/") with open(self.tempdir+"/bcur_data", "rb") as fin: b = bytearray(100) while True: l = fin.readinto(b) if l == 0: break fout.write(b, l) obj = { "title": "Transaction is signed!", "message": "Scan it with your wallet", } gc.collect() return self.tempdir+"/bcur_full", obj return elif cmd == LIST_WALLETS: wnames = json.dumps([w.name for w in self.wallets]) return BytesIO(wnames.encode()), {} elif cmd == ADD_WALLET: # read content, it's small desc = stream.read().decode().strip() w = self.parse_wallet(desc) res = await self.confirm_new_wallet(w, show_screen) if res: self.add_wallet(w) return elif cmd == VERIFY_ADDRESS: data = stream.read().decode().replace("bitcoin:", "") # should be of the form addr?index=N or similar if "index=" not in data or "?" not in data: raise WalletError("Can't verify address with unknown index") addr, rest = data.split("?") args = rest.split("&") idx = None for arg in args: if arg.startswith("index="): idx = int(arg[6:]) break w, _ = self.find_wallet_from_address(addr, index=idx) await show_screen(WalletScreen(w, self.network, idx)) return elif cmd == DERIVE_ADDRESS: arr = stream.read().split(b" ") redeem_script = None if len(arr) == 2: script_type, path = arr elif len(arr) == 3: script_type, path, redeem_script = arr else: raise WalletError("Too many arguments") paths = [p.decode() for p in path.split(b",")] if len(paths) == 0: raise WalletError("Invalid path argument") res = await self.showaddr( paths, script_type, redeem_script, show_screen=show_screen ) return BytesIO(res), {} else: raise WalletError("Unknown command")
def clear_testdir(): try: platform.delete_recursively(TEST_DIR, include_self=True) except: pass
def wipe(self): """ Delete all the contents of the app including the app folder itself. """ delete_recursively(self.path, include_self=True)
async def sign_psbt(self, stream, show_screen, encoding=BASE64_STREAM): if encoding == BASE64_STREAM: with open(self.tempdir + "/raw", "wb") as f: # read in chunks, write to ram file a2b_base64_stream(stream, f) with open(self.tempdir + "/raw", "rb") as f: psbt = PSBT.read_from(f, compress=True) # cleanup platform.delete_recursively(self.tempdir) else: psbt = PSBT.read_from(stream, compress=True) # check if all utxos are there and if there are custom sighashes sighash = SIGHASH.ALL custom_sighashes = [] for i, inp in enumerate(psbt.inputs): if ( not inp.is_verified ) and inp.witness_utxo is None and inp.non_witness_utxo is None: raise WalletError( "Invalid PSBT - missing previous transaction") if inp.sighash_type and inp.sighash_type != SIGHASH.ALL: custom_sighashes.append((i, inp.sighash_type)) if len(custom_sighashes) > 0: txt = [("Input %d: " % i) + SIGHASH_NAMES[sh] for (i, sh) in custom_sighashes] canceltxt = "Only sign ALL" if len(custom_sighashes) != len( psbt.inputs) else "Cancel" confirm = await show_screen( Prompt("Warning!", "\nCustom SIGHASH flags are used!\n\n" + "\n".join(txt), confirm_text="Sign anyway", cancel_text=canceltxt)) if confirm: sighash = None else: if len(custom_sighashes) == len(psbt.inputs): # nothing to sign return wallets, meta = self.parse_psbt(psbt=psbt) # there is an unknown wallet # wallet is a list of tuples: (wallet, amount) if None in [w[0] for w in wallets]: scr = Prompt( "Warning!", "\nUnknown wallet in inputs!\n\n\n" "Wallet for some inpunts is unknown! This means we can't verify change addresses.\n\n\n" "Hint:\nYou can cancel this transaction and import the wallet by scanning it's descriptor.\n\n\n" "Proceed to the transaction confirmation?", ) proceed = await show_screen(scr) if not proceed: return None spends = [] for w, amount in wallets: if w is None: name = "Unknown wallet" else: name = w.name spends.append('%.8f BTC\nfrom "%s"' % (amount / 1e8, name)) title = "Spending:\n" + "\n".join(spends) res = await show_screen(TransactionScreen(title, meta)) if res: self.show_loader(title="Signing transaction...") sigsStart = 0 for i, inp in enumerate(psbt.inputs): sigsStart += len(list(inp.partial_sigs.keys())) for w, _ in wallets: if w is None: continue # fill derivation paths from proprietary fields w.update_gaps(psbt=psbt) w.save(self.keystore) w.fill_psbt(psbt, self.keystore.fingerprint) if w.has_private_keys: w.sign_psbt(psbt, sighash) self.keystore.sign_psbt(psbt, sighash) # remove unnecessary stuff: out_psbt = PSBT(psbt.tx) sigsEnd = 0 for i, inp in enumerate(psbt.inputs): sigsEnd += len(list(inp.partial_sigs.keys())) out_psbt.inputs[i].partial_sigs = inp.partial_sigs del psbt gc.collect() if sigsEnd == sigsStart: raise WalletError( "We didn't add any signatures!\n\nMaybe you forgot to import the wallet?\n\nScan the wallet descriptor to import it." ) if encoding == BASE64_STREAM: # TODO: also use ram file txt = b2a_base64(out_psbt.serialize()).decode().strip() else: txt = out_psbt.serialize() return BytesIO(txt)