Ejemplo n.º 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
Ejemplo n.º 2
0
def parse_multisig(script):
    # Get m
    m = script[0] - 80
    if m < 1 or m > 15:
        return (False, None)

    # Get pubkeys and build HDNodePathType
    pubkeys = []
    offset = 1
    while True:
        pubkey_len = script[offset]
        if pubkey_len != 33:
            break
        offset += 1
        key = script[offset:offset + 33]
        offset += 33

        hd_node = proto.HDNodeType(depth=0, fingerprint=0, child_num=0, chain_code=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', public_key=key)
        pubkeys.append(proto.HDNodePathType(node=hd_node, address_n=[]))

    # Check things at the end
    n = script[offset] - 80
    if n != len(pubkeys):
        return (False, None)
    offset += 1
    op_cms = script[offset]
    if op_cms != 174:
        return (False, None)

    # Build MultisigRedeemScriptType and return it
    multisig = proto.MultisigRedeemScriptType(m=m, signatures=[b''] * n, pubkeys=pubkeys)
    return (True, multisig)
Ejemplo n.º 3
0
    def display_multisig_address(
        self,
        addr_type: AddressType,
        multisig: MultisigDescriptor,
    ) -> str:
        self._check_unlocked()

        der_pks = list(
            zip([p.get_pubkey_bytes(0) for p in multisig.pubkeys], multisig.pubkeys)
        )
        if multisig.is_sorted:
            der_pks = sorted(der_pks)

        pubkey_objs = []
        for pk, p in der_pks:
            if p.extkey is not None:
                xpub = p.extkey
                hd_node = messages.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,
                )
                pubkey_objs.append(
                    messages.HDNodePathType(
                        node=hd_node,
                        address_n=parse_path(
                            "m" + p.deriv_path if p.deriv_path is not None else ""
                        ),
                    )
                )
            else:
                hd_node = messages.HDNodeType(
                    depth=0,
                    fingerprint=0,
                    child_num=0,
                    chain_code=b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
                    public_key=pk,
                )
                pubkey_objs.append(messages.HDNodePathType(node=hd_node, address_n=[]))

        trezor_ms = messages.MultisigRedeemScriptType(
            m=multisig.thresh, signatures=[b""] * len(pubkey_objs), pubkeys=pubkey_objs
        )

        # Script type
        if addr_type == AddressType.SH_WIT:
            script_type = messages.InputScriptType.SPENDP2SHWITNESS
        elif addr_type == AddressType.WIT:
            script_type = messages.InputScriptType.SPENDWITNESS
        elif addr_type == AddressType.LEGACY:
            script_type = messages.InputScriptType.SPENDMULTISIG
        else:
            raise BadArgumentError("Unknown address type")

        for p in multisig.pubkeys:
            keypath = p.origin.get_derivation_path() if p.origin is not None else "m/"
            keypath += p.deriv_path if p.deriv_path is not None else ""
            path = parse_path(keypath)
            try:
                address = btc.get_address(
                    self.client,
                    self.coin_name,
                    path,
                    show_display=True,
                    script_type=script_type,
                    multisig=trezor_ms,
                )
                assert isinstance(address, str)
                return address
            except Exception:
                pass

        raise BadArgumentError("No path supplied matched device keys")
Ejemplo n.º 4
0
def parse_multisig(
    script: bytes,
    tx_xpubs: Dict[bytes, KeyOriginInfo],
    psbt_scope: Union[PartiallySignedInput, PartiallySignedOutput],
) -> Tuple[bool, Optional[messages.MultisigRedeemScriptType]]:
    # at least OP_M pub OP_N OP_CHECKMULTISIG
    if len(script) < 37:
        return (False, None)
    # Get m
    m = script[0] - 80
    if m < 1 or m > 15:
        return (False, None)

    # Get pubkeys and build HDNodePathType
    pubkeys = []
    offset = 1
    while True:
        pubkey_len = script[offset]
        if pubkey_len != 33:
            break
        offset += 1
        key = script[offset : offset + 33]
        offset += 33

        hd_node = messages.HDNodeType(
            depth=0,
            fingerprint=0,
            child_num=0,
            chain_code=b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
            public_key=key,
        )
        pubkeys.append(messages.HDNodePathType(node=hd_node, address_n=[]))

    # Check things at the end
    n = script[offset] - 80
    if n != len(pubkeys):
        return (False, None)
    offset += 1
    op_cms = script[offset]
    if op_cms != 174:
        return (False, None)

    # check if we know corresponding xpubs from global scope
    for pub in pubkeys:
        if pub.node.public_key in psbt_scope.hd_keypaths:
            derivation = psbt_scope.hd_keypaths[pub.node.public_key]
            for xpub in tx_xpubs:
                hd = ExtendedKey.deserialize(base58.encode(xpub + hash256(xpub)[:4]))
                origin = tx_xpubs[xpub]
                # check fingerprint and derivation
                if (origin.fingerprint == derivation.fingerprint) and (
                    origin.path == derivation.path[: len(origin.path)]
                ):
                    # all good - populate node and break
                    pub.address_n = list(derivation.path[len(origin.path) :])
                    pub.node = messages.HDNodeType(
                        depth=hd.depth,
                        fingerprint=int.from_bytes(hd.parent_fingerprint, "big"),
                        child_num=hd.child_num,
                        chain_code=hd.chaincode,
                        public_key=hd.pubkey,
                    )
                    break
    # Build MultisigRedeemScriptType and return it
    multisig = messages.MultisigRedeemScriptType(
        m=m, signatures=[b""] * n, pubkeys=pubkeys
    )
    return (True, multisig)
Ejemplo n.º 5
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")