예제 #1
0
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
예제 #2
0
    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
예제 #3
0
 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
예제 #4
0
    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")