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))
Example #2
0
 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)
Example #3
0
 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
Example #4
0
 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
Example #5
0
 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
Example #6
0
 def wipe(self):
     """Deletes all wallets info"""
     self.wallets = []
     self.path = None
     platform.delete_recursively(self.root_path)
Example #7
0
 def cleanup(self):
     if self.f is not None:
         self.f.close()
         self.f = None
     platform.delete_recursively(self.path)
Example #8
0
 def wipe(self, path):
     """Delete everything in path"""
     platform.delete_recursively(path)
Example #9
0
 def wipe(self):
     if self.path is None:
         raise WalletError("I don't know path...")
     delete_recursively(self.path, include_self=True)
Example #10
0
 def wipe(self):
     # TODO: wipe the smartcard as well?
     delete_recursively(fpath("/flash"))
     delete_recursively(fpath("/qspi"))
Example #11
0
 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")
Example #12
0
 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")
Example #13
0
def clear_testdir():
    try:
        platform.delete_recursively(TEST_DIR, include_self=True)
    except:
        pass
Example #14
0
 def wipe(self):
     """
     Delete all the contents of the app
     including the app folder itself.
     """
     delete_recursively(self.path, include_self=True)
Example #15
0
    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)