Exemplo n.º 1
0
    def get_tx(self, txhash):
        # method moved from TxApiInsight from which this class is derived. Reason: an error of casting vout['value']
        # to Decimal in the original method which occurres in some circumstances and causes the original value to be
        # distorted after the cast
        data = self.fetch_json('tx', txhash)

        t = trezor_proto.TransactionType()
        t.version = data['version']
        t.lock_time = data['locktime']

        for vin in data['vin']:
            i = t._add_inputs()
            if 'coinbase' in vin.keys():
                i.prev_hash = b"\0" * 32
                i.prev_index = 0xffffffff  # signed int -1
                i.script_sig = binascii.unhexlify(vin['coinbase'])
                i.sequence = vin['sequence']

            else:
                i.prev_hash = binascii.unhexlify(vin['txid'])
                i.prev_index = vin['vout']
                i.script_sig = binascii.unhexlify(vin['scriptSig']['hex'])
                i.sequence = vin['sequence']

        for vout in data['vout']:
            o = t._add_bin_outputs()
            o.amount = round(Decimal(vout['value'] * 100000000))  # fixed here
            o.script_pubkey = binascii.unhexlify(vout['scriptPubKey']['hex'])

        return t
Exemplo n.º 2
0
    def get_tx(self, txhash):
        tx = None
        for psbt_in in self.psbt.inputs:
            if psbt_in.non_witness_utxo and psbt_in.non_witness_utxo.sha256 == uint256_from_str(
                    binascii.unhexlify(txhash)[::-1]):
                tx = psbt_in.non_witness_utxo
        if not tx:
            raise ValueError("TX {} not found in PSBT".format(txhash))

        t = proto.TransactionType()
        t.version = tx.nVersion
        t.lock_time = tx.nLockTime

        for vin in tx.vin:
            i = t._add_inputs()
            i.prev_hash = ser_uint256(vin.prevout.hash)[::-1]
            i.prev_index = vin.prevout.n
            i.script_sig = vin.scriptSig
            i.sequence = vin.nSequence

        for vout in tx.vout:
            o = t._add_bin_outputs()
            o.amount = vout.nValue
            o.script_pubkey = vout.scriptPubKey

        return t
Exemplo n.º 3
0
 def json_to_tx(self, jtx):
     t = trezor_proto.TransactionType()
     t.version = jtx["version"]
     t.lock_time = jtx["locktime"]
     t.inputs = [self.json_to_input(input) for input in jtx["vin"]]
     t.bin_outputs = [self.json_to_bin_output(output) for output in jtx["vout"]]
     return t
Exemplo n.º 4
0
def make_transaction(tx) -> m.TransactionType:
    return m.TransactionType(
        version=tx.version,
        inputs=[make_input(txi) for txi in tx.inputs],
        outputs=[make_binoutput(txo) for txo in tx.outputs],
        lock_time=tx.lock_time,
    )
Exemplo n.º 5
0
 def copy_tx_meta(tx):
     tx_copy = trezor_proto.TransactionType(**tx)
     # clear fields
     tx_copy.inputs_cnt = len(tx.inputs)
     tx_copy.inputs = []
     tx_copy.outputs_cnt = len(tx.bin_outputs or tx.outputs)
     tx_copy.outputs = []
     tx_copy.bin_outputs = []
     tx_copy.extra_data_len = len(tx.extra_data or b"")
     tx_copy.extra_data = None
     return tx_copy
Exemplo n.º 6
0
def json_to_tx(coin, data):
    t = messages.TransactionType()
    t.version = data["version"]
    t.lock_time = data.get("locktime")

    if coin["decred"]:
        t.expiry = data["expiry"]

    t.inputs = [_json_to_input(coin, vin) for vin in data["vin"]]
    t.bin_outputs = [_json_to_bin_output(coin, vout) for vout in data["vout"]]

    # zcash extra data
    if is_zcash(coin) and t.version >= 2:
        joinsplit_cnt = len(data["vjoinsplit"])
        if joinsplit_cnt == 0:
            t.extra_data = b"\x00"
        elif joinsplit_cnt >= 253:
            # we assume cnt < 253, so we can treat varIntLen(cnt) as 1
            raise ValueError("Too many joinsplits")
        elif "hex" not in data:
            raise ValueError(
                "Raw TX data required for Zcash joinsplit transaction")
        else:
            rawtx = bytes.fromhex(data["hex"])
            extra_data_len = 1 + joinsplit_cnt * 1802 + 32 + 64
            t.extra_data = rawtx[-extra_data_len:]

    if is_polis(coin):
        dip2_type = data.get("type", 0)

        if t.version == 3 and dip2_type != 0:
            # It's a DIP2 special TX with payload

            if "extraPayloadSize" not in data or "extraPayload" not in data:
                raise ValueError("Payload data missing in DIP2 transaction")

            if data["extraPayloadSize"] * 2 != len(data["extraPayload"]):
                raise ValueError(
                    "extra_data_len (%d) does not match calculated length (%d)"
                    %
                    (data["extraPayloadSize"], len(data["extraPayload"]) * 2))
            t.extra_data = polis_utils.num_to_varint(
                data["extraPayloadSize"]) + bytes.fromhex(data["extraPayload"])

        # Trezor firmware doesn't understand the split of version and type, so let's mimic the
        # old serialization format
        t.version |= dip2_type << 16

    return t
Exemplo n.º 7
0
def forge_prevtx(
        vouts: Sequence[Tuple[str, int]],
        network: str = "mainnet") -> Tuple[bytes, messages.TransactionType]:
    """
    Forge a transaction with the given vouts.
    """
    bitcoin.SelectParams(network)
    input = messages.TxInputType(
        prev_hash=b"\x00" * 32,
        prev_index=0xFFFFFFFF,
        script_sig=b"\x00",
        sequence=0xFFFFFFFF,
    )
    outputs = [
        messages.TxOutputBinType(
            amount=amount,
            script_pubkey=bytes(CBitcoinAddress(address).to_scriptPubKey()),
        ) for address, amount in vouts
    ]
    tx = messages.TransactionType(
        version=1,
        inputs=[input],
        bin_outputs=outputs,
        lock_time=0,
    )

    cin = CTxIn(
        COutPoint(input.prev_hash, input.prev_index),
        CScript(input.script_sig),
        input.sequence,
    )
    couts = [
        CTxOut(output.amount, CScript(output.script_pubkey))
        for output in tx.bin_outputs
    ]
    txhash = CTransaction([cin], couts, tx.lock_time,
                          tx.version).GetTxid()[::-1]

    bitcoin.SelectParams("mainnet")

    return txhash, tx
Exemplo n.º 8
0
    def sign_tx(self, tx):

        # Get this devices master key fingerprint
        master_key = btc.get_public_node(self.client, [0])
        master_fp = get_xpub_fingerprint(master_key.xpub)

        # Do multiple passes for multisig
        passes = 1
        p = 0

        while p < passes:
            # Prepare inputs
            inputs = []
            to_ignore = [
            ]  # Note down which inputs whose signatures we're going to ignore
            for input_num, (psbt_in, txin) in py_enumerate(
                    list(zip(tx.inputs, tx.tx.vin))):
                txinputtype = proto.TxInputType()

                # Set the input stuff
                txinputtype.prev_hash = ser_uint256(txin.prevout.hash)[::-1]
                txinputtype.prev_index = txin.prevout.n
                txinputtype.sequence = txin.nSequence

                # Detrermine spend type
                scriptcode = b''
                if psbt_in.non_witness_utxo:
                    utxo = psbt_in.non_witness_utxo.vout[txin.prevout.n]
                    txinputtype.script_type = proto.InputScriptType.SPENDADDRESS
                    scriptcode = utxo.scriptPubKey
                    txinputtype.amount = psbt_in.non_witness_utxo.vout[
                        txin.prevout.n].nValue
                elif psbt_in.witness_utxo:
                    utxo = psbt_in.witness_utxo
                    # Check if the output is p2sh
                    if psbt_in.witness_utxo.is_p2sh():
                        txinputtype.script_type = proto.InputScriptType.SPENDP2SHWITNESS
                    else:
                        txinputtype.script_type = proto.InputScriptType.SPENDWITNESS
                    scriptcode = psbt_in.witness_utxo.scriptPubKey
                    txinputtype.amount = psbt_in.witness_utxo.nValue

                # Set the script
                if psbt_in.witness_script:
                    scriptcode = psbt_in.witness_script
                elif psbt_in.redeem_script:
                    scriptcode = psbt_in.redeem_script

                def ignore_input():
                    txinputtype.address_n = [0x80000000]
                    inputs.append(txinputtype)
                    to_ignore.append(input_num)

                # Check for multisig
                is_ms, multisig = parse_multisig(scriptcode)
                if is_ms:
                    # Add to txinputtype
                    txinputtype.multisig = multisig
                    if psbt_in.non_witness_utxo:
                        if utxo.is_p2sh:
                            txinputtype.script_type = proto.InputScriptType.SPENDMULTISIG
                        else:
                            # Cannot sign bare multisig, ignore it
                            ignore_input()
                            continue
                elif not is_ms and psbt_in.non_witness_utxo and not utxo.is_p2pkh:
                    # Cannot sign unknown spk, ignore it
                    ignore_input()
                    continue
                elif not is_ms and psbt_in.witness_utxo and psbt_in.witness_script:
                    # Cannot sign unknown witness script, ignore it
                    ignore_input()
                    continue

                # Find key to sign with
                found = False
                our_keys = 0
                for key in psbt_in.hd_keypaths.keys():
                    keypath = psbt_in.hd_keypaths[key]
                    if keypath[
                            0] == master_fp and key not in psbt_in.partial_sigs:
                        if not found:
                            txinputtype.address_n = keypath[1:]
                            found = True
                        our_keys += 1

                # Determine if we need to do more passes to sign everything
                if our_keys > passes:
                    passes = our_keys

                if not found:
                    # This input is not one of ours
                    ignore_input()
                    continue

                # append to inputs
                inputs.append(txinputtype)

            # address version byte
            if self.is_testnet:
                p2pkh_version = b'\x6f'
                p2sh_version = b'\xc4'
                bech32_hrp = 'tb'
            else:
                p2pkh_version = b'\x00'
                p2sh_version = b'\x05'
                bech32_hrp = 'bc'

            # prepare outputs
            outputs = []
            for out in tx.tx.vout:
                txoutput = proto.TxOutputType()
                txoutput.amount = out.nValue
                txoutput.script_type = proto.OutputScriptType.PAYTOADDRESS
                if out.is_p2pkh():
                    txoutput.address = to_address(out.scriptPubKey[3:23],
                                                  p2pkh_version)
                elif out.is_p2sh():
                    txoutput.address = to_address(out.scriptPubKey[2:22],
                                                  p2sh_version)
                else:
                    wit, ver, prog = out.is_witness()
                    if wit:
                        txoutput.address = bech32.encode(bech32_hrp, ver, prog)
                    else:
                        raise TypeError("Output is not an address")

                # append to outputs
                outputs.append(txoutput)

            # Prepare prev txs
            prevtxs = {}
            for psbt_in in tx.inputs:
                if psbt_in.non_witness_utxo:
                    prev = psbt_in.non_witness_utxo

                    t = proto.TransactionType()
                    t.version = prev.nVersion
                    t.lock_time = prev.nLockTime

                    for vin in prev.vin:
                        i = proto.TxInputType()
                        i.prev_hash = ser_uint256(vin.prevout.hash)[::-1]
                        i.prev_index = vin.prevout.n
                        i.script_sig = vin.scriptSig
                        i.sequence = vin.nSequence
                        t.inputs.append(i)

                    for vout in prev.vout:
                        o = proto.TxOutputBinType()
                        o.amount = vout.nValue
                        o.script_pubkey = vout.scriptPubKey
                        t.bin_outputs.append(o)
                    logging.debug(psbt_in.non_witness_utxo.hash)
                    prevtxs[ser_uint256(
                        psbt_in.non_witness_utxo.sha256)[::-1]] = t

            # Sign the transaction
            tx_details = proto.SignTx()
            tx_details.version = tx.tx.nVersion
            tx_details.lock_time = tx.tx.nLockTime
            if self.is_testnet:
                signed_tx = btc.sign_tx(self.client, "Testnet", inputs,
                                        outputs, tx_details, prevtxs)
            else:
                signed_tx = btc.sign_tx(self.client, "Bitcoin", inputs,
                                        outputs, tx_details, prevtxs)

            # Each input has one signature
            for input_num, (psbt_in, sig) in py_enumerate(
                    list(zip(tx.inputs, signed_tx[0]))):
                if input_num in to_ignore:
                    continue
                for pubkey in psbt_in.hd_keypaths.keys():
                    fp = psbt_in.hd_keypaths[pubkey][0]
                    if fp == master_fp and pubkey not in psbt_in.partial_sigs:
                        psbt_in.partial_sigs[pubkey] = sig + b'\x01'
                        break

            p += 1

        return {'psbt': tx.serialize()}
Exemplo n.º 9
0
def sign_tx(sig_percent,
            client,
            coin_name,
            inputs,
            outputs,
            details=None,
            prev_txes=None):
    # set up a transactions dict
    txes = {None: trezor_proto.TransactionType(inputs=inputs, outputs=outputs)}
    # preload all relevant transactions ahead of time
    for inp in inputs:
        if inp.script_type not in (
                trezor_proto.InputScriptType.SPENDP2SHWITNESS,
                trezor_proto.InputScriptType.SPENDWITNESS,
                trezor_proto.InputScriptType.EXTERNAL,
        ):
            try:
                prev_tx = prev_txes[inp.prev_hash]
            except Exception as e:
                raise ValueError("Could not retrieve prev_tx") from e
            if not isinstance(prev_tx, trezor_proto.TransactionType):
                raise ValueError("Invalid value for prev_tx") from None
            txes[inp.prev_hash] = prev_tx

    if details is None:
        signtx = trezor_proto.SignTx()
    else:
        signtx = details

    signtx.coin_name = coin_name
    signtx.inputs_count = len(inputs)
    signtx.outputs_count = len(outputs)

    res = client.call(signtx)

    # Prepare structure for signatures
    signatures = [None] * len(inputs)
    serialized_tx = b""

    def copy_tx_meta(tx):
        tx_copy = trezor_proto.TransactionType(**tx)
        # clear fields
        tx_copy.inputs_cnt = len(tx.inputs)
        tx_copy.inputs = []
        tx_copy.outputs_cnt = len(tx.bin_outputs or tx.outputs)
        tx_copy.outputs = []
        tx_copy.bin_outputs = []
        tx_copy.extra_data_len = len(tx.extra_data or b"")
        tx_copy.extra_data = None
        return tx_copy

    R = trezor_proto.RequestType

    percent = 0  # Used for signaling progress. 1-10 for inputs/outputs, 10-100 for sigs.
    sig_percent.emit(percent)
    while isinstance(res, trezor_proto.TxRequest):
        # If there's some part of signed transaction, let's add it
        if res.serialized:
            if res.serialized.serialized_tx:
                serialized_tx += res.serialized.serialized_tx

            if res.serialized.signature_index is not None:
                idx = res.serialized.signature_index
                sig = res.serialized.signature
                if signatures[idx] is not None:
                    raise ValueError("Signature for index %d already filled" %
                                     idx)
                signatures[idx] = sig
                # emit completion percent
                percent = 10 + int(90 * (idx + 1) / len(signatures))
                sig_percent.emit(percent)

        if res.request_type == R.TXFINISHED:
            break

        # Device asked for one more information, let's process it.
        current_tx = txes[res.details.tx_hash]

        if res.request_type == R.TXMETA:
            msg = copy_tx_meta(current_tx)
            res = client.call(trezor_proto.TxAck(tx=msg))

        elif res.request_type == R.TXINPUT:
            if percent == 0 or (res.details.request_index > 0
                                and percent < 10):
                percent = 1 + int(
                    8 * (res.details.request_index + 1) / len(inputs))
                sig_percent.emit(percent)
            msg = trezor_proto.TransactionType()
            msg.inputs = [current_tx.inputs[res.details.request_index]]
            res = client.call(trezor_proto.TxAck(tx=msg))

        elif res.request_type == R.TXOUTPUT:
            # Update just one percent then display additional waiting message (emitting -1)
            if percent == 9:
                percent += 1
                sig_percent.emit(percent)
                sig_percent.emit(-1)

            msg = trezor_proto.TransactionType()
            if res.details.tx_hash:
                msg.bin_outputs = [
                    current_tx.bin_outputs[res.details.request_index]
                ]
            else:
                msg.outputs = [current_tx.outputs[res.details.request_index]]

            res = client.call(trezor_proto.TxAck(tx=msg))

        elif res.request_type == R.TXEXTRADATA:
            o, l = res.details.extra_data_offset, res.details.extra_data_len
            msg = trezor_proto.TransactionType()
            msg.extra_data = current_tx.extra_data[o:o + l]
            res = client.call(trezor_proto.TxAck(tx=msg))

    if isinstance(res, trezor_proto.Failure):
        raise Exception("Signing failed")

    if not isinstance(res, trezor_proto.TxRequest):
        raise Exception("Unexpected message")

    if None in signatures:
        raise RuntimeError("Some signatures are missing!")

    return signatures, serialized_tx
Exemplo n.º 10
0
def json_to_tx(coin, data):
    t = messages.TransactionType()
    t.version = data["version"]
    t.lock_time = data.get("locktime")

    if coin["decred"]:
        t.expiry = data["expiry"]

    t.inputs = []
    for vin in data["vin"]:
        if "scriptSig" in vin and vin["scriptSig"]["hex"] == "c9":
            i = messages.TxInputType()
            i.prev_hash = b"\0" * 32
            i.prev_index = vin["sequence"]
            i.script_sig = bytes.fromhex(vin["scriptSig"]["hex"])
            i.sequence = vin["sequence"]
            t.inputs.append(i)
        else:
            t.inputs.append(_json_to_input(coin, vin))
    t.bin_outputs = [_json_to_bin_output(coin, vout) for vout in data["vout"]]

    # zcash extra data
    if is_zcash(coin) and t.version >= 2:
        joinsplit_cnt = len(data["vjoinsplit"])
        if joinsplit_cnt == 0:
            t.extra_data = b"\x00"
        elif joinsplit_cnt >= 253:
            # we assume cnt < 253, so we can treat varIntLen(cnt) as 1
            raise ValueError("Too many joinsplits")
        elif "hex" not in data:
            raise ValueError(
                "Raw TX data required for Zcash joinsplit transaction")
        else:
            rawtx = bytes.fromhex(data["hex"])
            extra_data_len = 1 + joinsplit_cnt * 1802 + 32 + 64
            t.extra_data = rawtx[-extra_data_len:]

    if is_extra_payload(coin):
        dip2_type = data.get("type", 0)

        if t.version == 3 and dip2_type != 0:
            # It's a DIP2 special TX with payload
            if dip2_type == DashTxType.SPEC_CB_TX:
                data["extraPayload"] = serialize_cbTx(data)
            elif dip2_type == DashTxType.LELANTUS_JSPLIT:
                data["extraPayload"] = serialize_Lelantus(data)
            else:
                raise NotImplementedError(
                    "Only spending of V3 coinbase outputs has been inplemented. "
                    "Please file an issue at https://github.com/firoorg/firo-masternode-tool/issues containing "
                    "the tx type=" + str(dip2_type))
            data["extraPayloadSize"] = len(data["extraPayload"]) >> 1

            if "extraPayloadSize" not in data or "extraPayload" not in data:
                raise ValueError("Payload data missing in DIP2 transaction")

            if data["extraPayloadSize"] * 2 != len(data["extraPayload"]):
                raise ValueError(
                    "extra_data_len (%d) does not match calculated length (%d)"
                    %
                    (data["extraPayloadSize"], len(data["extraPayload"]) * 2))
            t.extra_data = dash_utils.num_to_varint(
                data["extraPayloadSize"]) + bytes.fromhex(data["extraPayload"])

        # Trezor firmware doesn't understand the split of version and type, so let's mimic the
        # old serialization format
        t.version |= dip2_type << 16

    return t