async def sign_psbt(self, stream, show_screen): data = a2b_base64(stream.read()) psbt = PSBT.parse(data) wallets, meta = self.parse_psbt(psbt=psbt) spends = [] for w, amount in wallets: if w is None: name = "Unkown 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: for w, _ in wallets: if w is None: continue # fill derivation paths from proprietary fields w.update_gaps(psbt=psbt) w.save(self.keystore) psbt = w.fill_psbt(psbt, self.keystore.fingerprint) self.keystore.sign_psbt(psbt) # remove unnecessary stuff: out_psbt = PSBT(psbt.tx) for i, inp in enumerate(psbt.inputs): out_psbt.inputs[i].partial_sigs = inp.partial_sigs txt = b2a_base64(out_psbt.serialize()).decode().strip() return BytesIO(txt)
async def confirm_transaction_final(self, wallets, meta, show_screen): # build title for the tx screen spends = [] unit = "BTC" if self.network == "main" else "tBTC" for w in wallets: if w is None: name = "Unknown wallet" else: name = w.name amount = wallets[w].get("amount", 0) spends.append('%.8f %s\nfrom "%s"' % (amount / 1e8, unit, name)) title = "Inputs:\n" + "\n".join(spends) return await show_screen(TransactionScreen(title, meta))
async def sign_psbt(self, stream, show_screen): data = a2b_base64(stream.read()) psbt = PSBT.parse(data) wallet, meta = self.parse_psbt(psbt=psbt) res = await show_screen(TransactionScreen(wallet.name, meta)) if res: # fill derivation paths from proprietary fields wallet.update_gaps(psbt=psbt) wallet.save(self.keystore) psbt = wallet.fill_psbt(psbt, self.keystore.fingerprint) self.keystore.sign_psbt(psbt) # remove unnecessary stuff: out_psbt = PSBT(psbt.tx) for i, inp in enumerate(psbt.inputs): out_psbt.inputs[i].partial_sigs = inp.partial_sigs txt = b2a_base64(out_psbt.serialize()).decode().strip() return BytesIO(txt)
async def sign_psbt(self, stream, show_screen, encoding=BASE64_STREAM): if encoding == BASE64_STREAM: data = a2b_base64(stream.read()) psbt = PSBT.parse(data) else: psbt = PSBT.read_from(stream) # 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 inp.witness_utxo is None: if 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: txt = b2a_base64(out_psbt.serialize()).decode().strip() else: txt = out_psbt.serialize() return BytesIO(txt)