Ejemplo n.º 1
0
def parse_witness(vds, txin, full_parse: bool):
    n = vds.read_compact_size()
    if n == 0:
        txin['witness'] = '00'
        return
    if n == 0xffffffff:
        txin['value'] = vds.read_uint64()
        txin['witness_version'] = vds.read_uint16()
        n = vds.read_compact_size()
    # now 'n' is the number of items in the witness
    w = list(bh2u(vds.read_bytes(vds.read_compact_size())) for i in range(n))
    txin['witness'] = construct_witness(w)
    if not full_parse:
        return

    try:
        if txin.get('witness_version', 0) != 0:
            raise UnknownTxinType()
        if txin['type'] == 'coinbase':
            pass
        elif txin['type'] == 'address':
            pass
        elif txin['type'] == 'p2wsh-p2sh' or n > 2:
            witness_script_unsanitized = w[
                -1]  # for partial multisig txn, this has x_pubkeys
            try:
                m, n, x_pubkeys, pubkeys, witness_script = parse_redeemScript_multisig(
                    bfh(witness_script_unsanitized))
            except NotRecognizedRedeemScript:
                raise UnknownTxinType()
            txin['signatures'] = parse_sig(w[1:-1])
            txin['num_sig'] = m
            txin['x_pubkeys'] = x_pubkeys
            txin['pubkeys'] = pubkeys
            txin['witness_script'] = witness_script
            if not txin.get('scriptSig'):  # native segwit script
                txin['type'] = 'p2wsh'
                txin['address'] = bitcoin.script_to_p2wsh(witness_script)
        elif txin['type'] == 'p2wpkh-p2sh' or n == 2:
            txin['num_sig'] = 1
            txin['x_pubkeys'] = [w[1]]
            txin['pubkeys'] = [safe_parse_pubkey(w[1])]
            txin['signatures'] = parse_sig([w[0]])
            if not txin.get('scriptSig'):  # native segwit script
                txin['type'] = 'p2wpkh'
                txin['address'] = bitcoin.public_key_to_p2wpkh(
                    bfh(txin['pubkeys'][0]))
        else:
            raise UnknownTxinType()
    except UnknownTxinType:
        txin['type'] = 'unknown'
    except BaseException:
        txin['type'] = 'unknown'
        _logger.exception(f"failed to parse witness {txin.get('witness')}")
Ejemplo n.º 2
0
 def txid(self):
     self.deserialize()
     all_segwit = all(self.is_segwit_input(x) for x in self.inputs())
     if not all_segwit and not self.is_complete():
         return None
     ser = self.serialize_to_network(witness=False)
     return bh2u(sha256d(bfh(ser))[::-1])
Ejemplo n.º 3
0
 def _calc_bip143_shared_txdigest_fields(
         self) -> BIP143SharedTxDigestFields:
     inputs = self.inputs()
     outputs = self.outputs()
     hashPrevouts = bh2u(
         sha256d(
             bfh(''.join(self.serialize_outpoint(txin)
                         for txin in inputs))))
     hashSequence = bh2u(
         sha256d(
             bfh(''.join(
                 int_to_hex(txin.get('sequence', 0xffffffff - 1), 4)
                 for txin in inputs))))
     hashOutputs = bh2u(
         sha256d(bfh(''.join(self.serialize_output(o) for o in outputs))))
     return BIP143SharedTxDigestFields(hashPrevouts=hashPrevouts,
                                       hashSequence=hashSequence,
                                       hashOutputs=hashOutputs)
Ejemplo n.º 4
0
    def update_signatures(self, signatures: Sequence[str]):
        """Add new signatures to a transaction

        `signatures` is expected to be a list of sigs with signatures[i]
        intended for self._inputs[i].
        This is used by the Trezor, KeepKey an Safe-T plugins.
        """
        if self.is_complete():
            return
        if len(self.inputs()) != len(signatures):
            raise Exception('expected {} signatures; got {}'.format(
                len(self.inputs()), len(signatures)))
        for i, txin in enumerate(self.inputs()):
            pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
            sig = signatures[i]
            if sig in txin.get('signatures'):
                continue
            pre_hash = sha256d(bfh(self.serialize_preimage(i)))
            sig_string = ecc.sig_string_from_der_sig(bfh(sig[:-2]))
            for recid in range(4):
                try:
                    public_key = ecc.ECPubkey.from_sig_string(
                        sig_string, recid, pre_hash)
                except ecc.InvalidECPointException:
                    # the point might not be on the curve for some recid values
                    continue
                pubkey_hex = public_key.get_public_key_hex(compressed=True)
                if pubkey_hex in pubkeys:
                    try:
                        public_key.verify_message_hash(sig_string, pre_hash)
                    except Exception:
                        _logger.exception('')
                        continue
                    j = pubkeys.index(pubkey_hex)
                    _logger.info(f"adding sig {i} {j} {pubkey_hex} {sig}")
                    self.add_signature_to_txin(i, j, sig)
                    break
        # redo raw
        self.raw = self.serialize()
Ejemplo n.º 5
0
def tx_from_str(txt: str) -> str:
    """Sanitizes tx-describing input (json or raw hex or base43) into
    raw hex transaction."""
    assert isinstance(txt, str), f"txt must be str, not {type(txt)}"
    txt = txt.strip()
    if not txt:
        raise ValueError("empty string")
    # try hex
    try:
        bfh(txt)
        return txt
    except:
        pass
    # try base43
    try:
        return base_decode(txt, length=None, base=43).hex()
    except:
        pass
    # try json
    import json
    tx_dict = json.loads(str(txt))
    assert "hex" in tx_dict.keys()
    return tx_dict["hex"]
Ejemplo n.º 6
0
 def sign_txin(self,
               txin_index,
               privkey_bytes,
               *,
               bip143_shared_txdigest_fields=None) -> str:
     pre_hash = sha256d(
         bfh(
             self.serialize_preimage(
                 txin_index,
                 bip143_shared_txdigest_fields=bip143_shared_txdigest_fields
             )))
     privkey = ecc.ECPrivkey(privkey_bytes)
     sig = privkey.sign_transaction(pre_hash)
     sig = bh2u(sig) + '01'
     return sig
Ejemplo n.º 7
0
def xpubkey_to_address(x_pubkey):
    if x_pubkey[0:2] == 'fd':
        address = bitcoin.script_to_address(x_pubkey[2:])
        return x_pubkey, address
    if x_pubkey[0:2] in ['02', '03', '04']:
        pubkey = x_pubkey
    elif x_pubkey[0:2] == 'ff':
        xpub, s = parse_xpubkey(x_pubkey)
        pubkey = get_pubkey_from_xpub(xpub, s)
    else:
        raise BitcoinException("Cannot parse pubkey. prefix: {}".format(
            x_pubkey[0:2]))
    if pubkey:
        address = public_key_to_p2pkh(bfh(pubkey))
    return pubkey, address
Ejemplo n.º 8
0
    def get_preimage_script(self, txin):
        preimage_script = txin.get('preimage_script', None)
        if preimage_script is not None:
            return preimage_script

        pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
        if txin['type'] == 'p2pkh':
            return bitcoin.address_to_script(txin['address'])
        elif txin['type'] in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
            return multisig_script(pubkeys, txin['num_sig'])
        elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
            pubkey = pubkeys[0]
            pkh = bh2u(hash_160(bfh(pubkey)))
            return '76a9' + push_script(pkh) + '88ac'
        elif txin['type'] == 'p2pk':
            pubkey = pubkeys[0]
            return bitcoin.public_key_to_p2pk_script(pubkey)
        else:
            raise TypeError('Unknown txin type', txin['type'])
Ejemplo n.º 9
0
def parse_xpubkey(pubkey):
    # type + xpub + derivation
    assert pubkey[0:2] == 'ff'
    pk = bfh(pubkey)
    # xpub:
    pk = pk[1:]
    xkey = bitcoin.EncodeBase58Check(pk[0:78])
    # derivation:
    dd = pk[78:]
    s = []
    while dd:
        # 2 bytes for derivation path index
        n = int.from_bytes(dd[0:2], byteorder="little")
        dd = dd[2:]
        # in case of overflow, drop these 2 bytes; and use next 4 bytes instead
        if n == 0xffff:
            n = int.from_bytes(dd[0:4], byteorder="little")
            dd = dd[4:]
        s.append(n)
    assert len(s) == 2
    return xkey, s
Ejemplo n.º 10
0
def deserialize(raw: str, force_full_parse=False) -> dict:
    raw_bytes = bfh(raw)
    d = {}
    if raw_bytes[:5] == PARTIAL_TXN_HEADER_MAGIC:
        d['partial'] = is_partial = True
        partial_format_version = raw_bytes[5]
        if partial_format_version != 0:
            raise SerializationError(
                'unknown tx partial serialization format version: {}'.format(
                    partial_format_version))
        raw_bytes = raw_bytes[6:]
    else:
        d['partial'] = is_partial = False
    full_parse = force_full_parse or is_partial
    vds = BCDataStream()
    vds.write(raw_bytes)
    d['version'] = vds.read_int32()
    n_vin = vds.read_compact_size()
    is_segwit = (n_vin == 0)
    if is_segwit:
        marker = vds.read_bytes(1)
        if marker != b'\x01':
            raise ValueError('invalid txn marker byte: {}'.format(marker))
        n_vin = vds.read_compact_size()
    d['segwit_ser'] = is_segwit
    d['inputs'] = [
        parse_input(vds, full_parse=full_parse) for i in range(n_vin)
    ]
    n_vout = vds.read_compact_size()
    d['outputs'] = [parse_output(vds, i) for i in range(n_vout)]
    if is_segwit:
        for i in range(n_vin):
            txin = d['inputs'][i]
            parse_witness(vds, txin, full_parse=full_parse)
    d['lockTime'] = vds.read_uint32()
    if vds.can_read_more():
        raise SerializationError('extra junk at the end')
    return d
Ejemplo n.º 11
0
def parse_redeemScript_multisig(redeem_script: bytes):
    try:
        dec2 = [x for x in script_GetOp(redeem_script)]
    except MalformedBitcoinScript:
        raise NotRecognizedRedeemScript()
    try:
        m = dec2[0][0] - opcodes.OP_1 + 1
        n = dec2[-2][0] - opcodes.OP_1 + 1
    except IndexError:
        raise NotRecognizedRedeemScript()
    op_m = opcodes.OP_1 + m - 1
    op_n = opcodes.OP_1 + n - 1
    match_multisig = [op_m] + [OPPushDataGeneric] * n + [
        op_n, opcodes.OP_CHECKMULTISIG
    ]
    if not match_decoded(dec2, match_multisig):
        raise NotRecognizedRedeemScript()
    x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]]
    pubkeys = [safe_parse_pubkey(x) for x in x_pubkeys]
    redeem_script2 = bfh(multisig_script(x_pubkeys, m))
    if redeem_script2 != redeem_script:
        raise NotRecognizedRedeemScript()
    redeem_script_sanitized = multisig_script(pubkeys, m)
    return m, n, x_pubkeys, pubkeys, redeem_script_sanitized
Ejemplo n.º 12
0
def parse_scriptSig(d, _bytes):
    try:
        decoded = [x for x in script_GetOp(_bytes)]
    except Exception as e:
        # coinbase transactions raise an exception
        _logger.info(
            f"parse_scriptSig: cannot find address in input script (coinbase?) {bh2u(_bytes)}"
        )
        return

    match = [OPPushDataGeneric]
    if match_decoded(decoded, match):
        item = decoded[0][1]
        if item[0] == 0:
            # segwit embedded into p2sh
            # witness version 0
            d['address'] = bitcoin.hash160_to_p2sh(hash_160(item))
            if len(item) == 22:
                d['type'] = 'p2wpkh-p2sh'
            elif len(item) == 34:
                d['type'] = 'p2wsh-p2sh'
            else:
                _logger.info(f"unrecognized txin type {bh2u(item)}")
        elif opcodes.OP_1 <= item[0] <= opcodes.OP_16:
            # segwit embedded into p2sh
            # witness version 1-16
            pass
        else:
            # assert item[0] == 0x30
            # pay-to-pubkey
            d['type'] = 'p2pk'
            d['address'] = "(pubkey)"
            d['signatures'] = [bh2u(item)]
            d['num_sig'] = 1
            d['x_pubkeys'] = ["(pubkey)"]
            d['pubkeys'] = ["(pubkey)"]
        return

    # p2pkh TxIn transactions push a signature
    # (71-73 bytes) and then their public key
    # (33 or 65 bytes) onto the stack:
    match = [OPPushDataGeneric, OPPushDataGeneric]
    if match_decoded(decoded, match):
        sig = bh2u(decoded[0][1])
        x_pubkey = bh2u(decoded[1][1])
        try:
            signatures = parse_sig([sig])
            pubkey, address = xpubkey_to_address(x_pubkey)
        except:
            _logger.info(
                f"parse_scriptSig: cannot find address in input script (p2pkh?) {bh2u(_bytes)}"
            )
            return
        d['type'] = 'p2pkh'
        d['signatures'] = signatures
        d['x_pubkeys'] = [x_pubkey]
        d['num_sig'] = 1
        d['pubkeys'] = [pubkey]
        d['address'] = address
        return

    # p2sh transaction, m of n
    match = [opcodes.OP_0] + [OPPushDataGeneric] * (len(decoded) - 1)
    if match_decoded(decoded, match):
        x_sig = [bh2u(x[1]) for x in decoded[1:-1]]
        redeem_script_unsanitized = decoded[-1][
            1]  # for partial multisig txn, this has x_pubkeys
        try:
            m, n, x_pubkeys, pubkeys, redeem_script = parse_redeemScript_multisig(
                redeem_script_unsanitized)
        except NotRecognizedRedeemScript:
            _logger.info(
                f"parse_scriptSig: cannot find address in input script (p2sh?) {bh2u(_bytes)}"
            )

            # we could still guess:
            # d['address'] = hash160_to_p2sh(hash_160(decoded[-1][1]))
            return
        # write result in d
        d['type'] = 'p2sh'
        d['num_sig'] = m
        d['signatures'] = parse_sig(x_sig)
        d['x_pubkeys'] = x_pubkeys
        d['pubkeys'] = pubkeys
        d['redeem_script'] = redeem_script
        d['address'] = hash160_to_p2sh(hash_160(bfh(redeem_script)))
        return

    # custom partial format for imported addresses
    match = [opcodes.OP_INVALIDOPCODE, opcodes.OP_0, OPPushDataGeneric]
    if match_decoded(decoded, match):
        x_pubkey = bh2u(decoded[2][1])
        pubkey, address = xpubkey_to_address(x_pubkey)
        d['type'] = 'address'
        d['address'] = address
        d['num_sig'] = 1
        d['x_pubkeys'] = [x_pubkey]
        d['pubkeys'] = None  # get_sorted_pubkeys will populate this
        d['signatures'] = [None]
        return

    _logger.info(
        f"parse_scriptSig: cannot find address in input script (unknown) {bh2u(_bytes)}"
    )
Ejemplo n.º 13
0
 def wtxid(self):
     self.deserialize()
     if not self.is_complete():
         return None
     ser = self.serialize_to_network(witness=True)
     return bh2u(sha256d(bfh(ser))[::-1])
Ejemplo n.º 14
0
 def serialize_outpoint(self, txin):
     return bh2u(bfh(txin['prevout_hash'])[::-1]) + int_to_hex(
         txin['prevout_n'], 4)