def parse_multisig_xpubs(tx, psbt_in_out, multisig): try: old_pubs = [k.node.public_key for k in multisig.pubkeys] xpubs = [xpub for xpub in tx.unknown.keys() if xpub.startswith(b"\x01")] derivations = [tx.unknown[xpub] for xpub in xpubs] # unpack derivations = [list(struct.unpack("<" + "I" * (len(value) // 4), value)) for value in derivations] new_pubs = [] for pub in old_pubs: # derivation der = list(psbt_in_out.hd_keypaths[pub]) for i, derivation in py_enumerate(derivations): if der[0] == derivation[0]: idx = i for i in range(len(derivation)): if der[i] != derivation[i]: # derivations mismatch return multisig break xpub = xpubs[idx][1:] address_n = der[len(derivations[idx]):] xpub_obj = ExtendedKey() xpub_obj.deserialize(base58_encode(xpub + hash256(xpub)[:4])) hd_node = proto.HDNodeType(depth=xpub_obj.depth, fingerprint=der[0], child_num=xpub_obj.child_num, chain_code=xpub_obj.chaincode, public_key=xpub_obj.pubkey) new_pub = proto.HDNodePathType(node=hd_node, address_n=address_n) new_pubs.append(new_pub) return proto.MultisigRedeemScriptType(m=multisig.m, signatures=multisig.signatures, pubkeys=new_pubs) except: # If not all necessary data is available or malformatted, return the original multisig return multisig
def get_pubkey_at_path(self, path): if not check_keypath(path): raise BadArgumentError("Invalid keypath") path = path[2:] path = path.replace("h", "'") path = path.replace("H", "'") # This call returns raw uncompressed pubkey, chaincode pubkey = self.app.getWalletPublicKey(path) if path != "": parent_path = "" for ind in path.split("/")[:-1]: parent_path += ind + "/" parent_path = parent_path[:-1] # Get parent key fingerprint parent = self.app.getWalletPublicKey(parent_path) fpr = hash160(compress_public_key(parent["publicKey"]))[:4] # Compute child info childstr = path.split("/")[-1] hard = 0 if childstr[-1] == "'" or childstr[-1] == "h" or childstr[ -1] == "H": childstr = childstr[:-1] hard = 0x80000000 child = struct.pack(">I", int(childstr) + hard) # Special case for m else: child = bytearray.fromhex("00000000") fpr = child chainCode = pubkey["chainCode"] publicKey = compress_public_key(pubkey["publicKey"]) depth = len(path.split("/")) if len(path) > 0 else 0 depth = struct.pack("B", depth) if self.is_testnet: version = bytearray.fromhex("043587CF") else: version = bytearray.fromhex("0488B21E") extkey = version + depth + fpr + child + chainCode + publicKey checksum = hash256(extkey)[:4] xpub = base58.encode(extkey + checksum) result = {"xpub": xpub} if self.expert: xpub_obj = ExtendedKey() xpub_obj.deserialize(xpub) result.update(xpub_obj.get_printable_dict()) return result
def get_pubkey_at_path(self, path): self._check_unlocked() try: expanded_path = tools.parse_path(path) except ValueError as e: raise BadArgumentError(str(e)) output = btc.get_public_node(self.client, expanded_path, coin_name=self.coin_name) if self.is_testnet: result = {'xpub': xpub_main_2_test(output.xpub)} else: result = {'xpub': output.xpub} if self.expert: xpub_obj = ExtendedKey() xpub_obj.deserialize(output.xpub) result.update(xpub_obj.get_printable_dict()) return result
def display_address(self, keypath, p2sh_p2wpkh, bech32, redeem_script=None, descriptor=None): self._check_unlocked() # descriptor means multisig with xpubs if descriptor: pubkeys = [] xpub = ExtendedKey() for i in range(0, descriptor.multisig_N): xpub.deserialize(descriptor.base_key[i]) hd_node = proto.HDNodeType( depth=xpub.depth, fingerprint=int.from_bytes(xpub.parent_fingerprint, "big"), child_num=xpub.child_num, chain_code=xpub.chaincode, public_key=xpub.pubkey, ) pubkeys.append( proto.HDNodePathType( node=hd_node, address_n=tools.parse_path("m" + descriptor.path_suffix[i]), )) multisig = proto.MultisigRedeemScriptType( m=int(descriptor.multisig_M), signatures=[b""] * int(descriptor.multisig_N), pubkeys=pubkeys, ) # redeem_script means p2sh/multisig elif redeem_script: # Get multisig object required by Trezor's get_address multisig = parse_multisig(bytes.fromhex(redeem_script)) if not multisig[0]: raise BadArgumentError( "The redeem script provided is not a multisig. Only multisig scripts can be displayed." ) multisig = multisig[1] else: multisig = None # Script type if p2sh_p2wpkh: script_type = proto.InputScriptType.SPENDP2SHWITNESS elif bech32: script_type = proto.InputScriptType.SPENDWITNESS elif redeem_script: script_type = proto.InputScriptType.SPENDMULTISIG else: script_type = proto.InputScriptType.SPENDADDRESS # convert device fingerprint to 'm' if exists in path keypath = keypath.replace(self.get_master_fingerprint_hex(), "m") for path in keypath.split(","): if len(path.split("/")[0]) == 8: path = path.split("/", 1)[1] expanded_path = tools.parse_path(path) try: address = btc.get_address( self.client, self.coin_name, expanded_path, show_display=True, script_type=script_type, multisig=multisig, ) return {"address": address} except: pass raise BadArgumentError("No path supplied matched device keys")