Ejemplo n.º 1
0
def check_multisig_type(script):
    """
    Checks whether a given script is a multisig one. If it is multisig, return type (m and n values).

    :param script: The script to be checked.
    :type script: str
    :return: "multisig-m-n" or False
    """

    if len(OutputScript.deserialize(script).split()) > 2:
        m = OutputScript.deserialize(script).split()[0]
        n = OutputScript.deserialize(script).split()[-2]
        op_multisig = OutputScript.deserialize(script).split()[-1]

        if op_multisig == "OP_CHECKMULTISIG" and script[2:4] in ["21", "41"]:
            return "multisig-" + str(m) + "-" + str(n)

    return False
Ejemplo n.º 2
0
def decompress_script(compressed_script, script_type):
    """ Takes CScript as stored in leveldb and returns it in uncompressed form
    (de)compression scheme is defined in bitcoin/src/compressor.cpp

    :param compressed_script: raw script bytes hexlified (data in decode_utxo)
    :type compressed_script: str
    :param script_type: first byte of script data (out_type in decode_utxo)
    :type script_type: int
    :return: the decompressed CScript
    :rtype: str
    """

    if script_type == 0:
        if len(compressed_script) != 40:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2PKH(compressed_script, hash160=True)

    elif script_type == 1:
        if len(compressed_script) != 40:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2SH(compressed_script)

    elif script_type in [2, 3]:
        if len(compressed_script) != 66:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2PK(compressed_script)

    elif script_type in [4, 5]:
        if len(compressed_script) != 66:
            raise Exception("Compressed script has wrong size")
        prefix = format(script_type - 2, '02')
        script = OutputScript.P2PK(
            get_uncompressed_pk(prefix + compressed_script[2:]))

    else:
        assert len(compressed_script) / 2 == script_type - NSPECIALSCRIPTS
        script = OutputScript.from_hex(compressed_script)

    return script.content
Ejemplo n.º 3
0
def decompress_script(compressed_script, script_type):
    """ Takes CScript as stored in leveldb and returns it in uncompressed form
    (de)compression scheme is defined in bitcoin/src/compressor.cpp

    :param compressed_script: raw script bytes hexlified (data in decode_utxo)
    :type compressed_script: str
    :param script_type: first byte of script data (out_type in decode_utxo)
    :type script_type: int
    :return: the decompressed CScript
    :rtype: str
    """

    if script_type == 0:
        if len(compressed_script) != 40:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2PKH(compressed_script)

    elif script_type == 1:
        if len(compressed_script) != 40:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2SH(compressed_script)

    elif script_type in [2, 3]:
        if len(compressed_script) != 66:
            raise Exception("Compressed script has wrong size")
        script = OutputScript.P2PK(compressed_script)

    elif script_type in [4, 5]:
        # pfx = chr(int(script_bytes[:2], 16) - 2)
        # script = OutputScript.P2PK(hexlify(pfx) + script_bytes[2:])
        # ToDO: Create P2PK script using decompressed PK (decompress_pk needs to be implemented).
        pass

    else:
        assert len(compressed_script) == script_type - (NSPECIALSCRIPTS * 2)
        script = OutputScript.from_hex(compressed_script)

    return script.serialize()
Ejemplo n.º 4
0
    def deserialize(cls, hex_tx):
        """ Builds a transaction object from the hexadecimal serialization format of a transaction that
        could be obtained, for example, from a blockexplorer.

        :param hex_tx: Hexadecimal serialized transaction.
        :type hex_tx: hex str
        :return: The transaction build using the provided hex serialized transaction.
        :rtype: TX
        """

        tx = cls()
        tx.hex = hex_tx

        tx.version = int(change_endianness(parse_element(tx, 4)), 16)

        # INPUTS
        tx.inputs = int(parse_varint(tx), 16)

        for i in range(tx.inputs):
            tx.prev_tx_id.append(change_endianness(parse_element(tx, 32)))
            tx.prev_out_index.append(
                int(change_endianness(parse_element(tx, 4)), 16))
            # ScriptSig
            tx.scriptSig_len.append(int(parse_varint(tx), 16))
            tx.scriptSig.append(
                InputScript.from_hex(parse_element(tx, tx.scriptSig_len[i])))
            tx.nSequence.append(int(parse_element(tx, 4), 16))

        # OUTPUTS
        tx.outputs = int(parse_varint(tx), 16)

        for i in range(tx.outputs):
            tx.value.append(int(change_endianness(parse_element(tx, 8)), 16))
            # ScriptPubKey
            tx.scriptPubKey_len.append(int(parse_varint(tx), 16))
            tx.scriptPubKey.append(
                OutputScript.from_hex(parse_element(tx,
                                                    tx.scriptPubKey_len[i])))

        tx.nLockTime = int(parse_element(tx, 4), 16)

        if tx.offset != len(tx.hex):
            raise Exception(
                "There is some error in the serialized transaction passed as input. Transaction can't"
                " be built")
        else:
            tx.offset = 0

        return tx
Ejemplo n.º 5
0
    def signature_format(self,
                         index,
                         hashflag=SIGHASH_ALL,
                         orphan=False,
                         network='test'):
        """ Builds the signature format an unsigned transaction has to follow in order to be signed. Basically empties
        every InputScript field but the one to be signed, identified by index, that will be filled with the OutputScript
        from the UTXO that will be redeemed.
        The format of the OutputScripts will depend on the hashflag:
            - SIGHASH_ALL leaves OutputScript unchanged.
            - SIGHASH_SINGLE should sign each input with the output of the same index (not implemented yet).
            - SIGHASH_NONE empies all the outputs.
            - SIGHASH_ANYONECANPAY not sure about what should do (obviously not implemented yet).
        :param index: The index of the input to be signed.
        :type index: int
        :param hashflag: Hash type to be used, see above description for further information.
        :type hashflag: int
        :param orphan: Whether the input is orphan or not. Orphan inputs must provide an OutputScript that matches the
        utxo to be redeemed.
        :type orphan: OutputScript
        :param network: Network into which the transaction will be published (either mainnet or testnet).
        :type network: str
        :return: Transaction properly formatted to be signed.
        :rtype TX
        """

        tx = deepcopy(self)
        for i in range(tx.inputs):
            if i is index:
                if not orphan:
                    script, t = get_prev_ScriptPubKey(tx.prev_tx_id[i],
                                                      tx.prev_out_index[i],
                                                      network)
                    # Once we get the previous UTXO script, the inputScript is temporarily set to it in order to sign
                    # the transaction.
                    tx.scriptSig[i] = InputScript.from_hex(script)
                    tx.scriptSig[i].type = t
                else:
                    # If input to be signed is orphan, the orphan InputScript is used when signing the transaction.
                    tx.scriptSig[i] = orphan
                tx.scriptSig_len[i] = len(tx.scriptSig[i].content) / 2
            elif tx.scriptSig[i].content != "":
                # All other scriptSig fields are emptied and their length is set to 0.
                tx.scriptSig[i] = InputScript()
                tx.scriptSig_len[i] = len(tx.scriptSig[i].content) / 2

        if hashflag is SIGHASH_SINGLE:
            # First we checks if the input that we are trying to sign has a corresponding output, if so, the execution
            # can continue. Otherwise, we abort the signature process since it could lead to a irreversible lose of
            # funds due to a bug in SIGHASH_SINGLE.
            # https://bitcointalk.org/index.php?topic=260595

            if index >= tx.outputs:
                raise Exception(
                    "You are trying to use SIGHASH_SINGLE to sign an input that does not have a "
                    "corresponding output (" + str(index) +
                    "). This could lead to a irreversible lose "
                    "of funds. Signature process aborted.")
            # Otherwise, all outputs will set to empty scripts but the ith one (identified by index),
            # since SIGHASH_SINGLE should only sign the ith input with the ith output.
            else:
                # How to properly deal with SIGHASH_SINGLE signature format extracted from:
                # https://github.com/bitcoin/bitcoin/blob/3192e5278a/test/functional/test_framework/script.py#L869

                # First we backup the output that we will sign,
                t_script = tx.scriptPubKey[index]
                t_size = tx.scriptPubKey_len[index]
                t_value = tx.value[index]

                # Then, we delete every single output.
                tx.scriptPubKey = []
                tx.scriptPubKey_len = []
                tx.value = []
                for o in range(index):
                    # Once the all outputs have been deleted, we create empty outputs for every single index before
                    # the one that will be signed. Furthermore, the value of the output if set to maximum (2^64-1)
                    tx.scriptPubKey.append(OutputScript())
                    tx.scriptPubKey_len.append(
                        len(tx.scriptPubKey[o].content) / 2)
                    tx.value.append(pow(2, 64) - 1)

                # Once we reach the index of the output that will be signed, we restore it with the one that we backed
                # up before.
                tx.scriptPubKey.append(t_script)
                tx.scriptPubKey_len.append(t_size)
                tx.value.append(t_value)

                # Finally, we recalculate the number of outputs for the signature format.
                # Notice that each signature format will have index number of outputs! Otherwise it will be invalid.
                tx.outputs = len(tx.scriptPubKey)

        elif hashflag is SIGHASH_NONE:
            # Empty all the scriptPubKeys and set the length and the output counter to 0.
            tx.outputs = 0
            tx.scriptPubKey = OutputScript()
            tx.scriptPubKey_len = len(tx.scriptPubKey.content) / 2

        elif hashflag is SIGHASH_ANYONECANPAY:
            # ToDo: Implement SIGHASH_ANYONECANPAY
            pass

        if hashflag in [SIGHASH_SINGLE, SIGHASH_NONE]:
            # All the nSequence from inputs except for the current one (index) is set to 0.
            # https://github.com/bitcoin/bitcoin/blob/3192e5278a/test/functional/test_framework/script.py#L880
            for i in range(tx.inputs):
                if i is not index:
                    tx.nSequence[i] = 0

        return tx
Ejemplo n.º 6
0
    def build_from_io(cls,
                      prev_tx_id,
                      prev_out_index,
                      value,
                      outputs,
                      fees=None,
                      network='test'):
        """ Builds a transaction from a collection of inputs and outputs, such as previous transactions references and
        output references (either public keys, Bitcoin addresses, list of public keys (for multisig transactions), etc).
        This builder leaves the transaction ready to sign, so its the one to be used in most cases
        (Standard transactions).
        outputs format:
        P2PKH -> Bitcoin address, or list of Bitcoin addresses.
        e.g: output = btc_addr or output = [btc_addr0, btc_addr1, ...]
        P2PK -> Serialized Public key, or list of serialized pubic keys. (use keys.serialize_pk)
        e.g: output = pk or output = [pk0, pk1, ...]
        P2MS -> List of int (m) and public keys, or list of lists of int (m_i) and public keys. m represent the m-of-n
        number of public keys needed to redeem the transaction.
        e.g: output = [n, pk0, pk1, ...] or output = [[n_0, pk0_0, pk0_1, ...], [n_1, pk1_0, pk1_1, ...], ...]
        P2SH -> script hash (hash160 str hex) or list of hash 160s.
        e.g: output = da1745e9b549bd0bfa1a569971c77eba30cd5a4b or output = [da1745e9b549bd0bfa1a569971c77eba30cd5a4b,
        ...]
        :param prev_tx_id: Previous transaction id.
        :type prev_tx_id: either str or list of str
        :param prev_out_index: Previous output index. Together with prev_tx_id represent the UTXOs the current
        transaction is aiming to redeem.
        :type prev_out_index: either str or list of str
        :param value: Value in Satoshis to be spent.
        :type value: either int or list of int
        :param outputs: Information to build the output of the transaction.
        :type outputs: See above outputs format.
        :param fees: Fees that will be applied to the transaction. If set, fees will be subtracted from the last output.
        :type fees: int
        :param network: Network into which the transaction will be published (either mainnet or testnet).
        :type network: str
        :return: Transaction build with the input and output provided data.
        :rtype: TX
        """

        ins = []
        outs = []

        # Normalize all parameters
        if isinstance(prev_tx_id, str):
            prev_tx_id = [prev_tx_id]
        if isinstance(prev_out_index, int):
            prev_out_index = [prev_out_index]
        if isinstance(value, int):
            value = [value]
        if isinstance(outputs, str) or (isinstance(outputs, list)
                                        and isinstance(outputs[0], int)):
            outputs = [outputs]

        # If fees have been set, subtract them from the final value. Otherwise, assume they have been already
        # subtracted when specifying the amounts.
        if fees:
            value[-1] -= fees

        if len(prev_tx_id) != len(prev_out_index):
            raise Exception(
                "Previous transaction id and index number of elements must match. "
                + str(len(prev_tx_id)) + "!= " + str(len(prev_out_index)))
        elif len(value) != len(outputs):
            raise Exception(
                "Each output must have set a Satoshi amount. Use 0 if no value is going to be transferred."
            )

        for o in outputs:
            # Multisig outputs are passes ad an integer m representing the m-of-n transaction, amb m public keys.
            if isinstance(o, list) and o[0] in range(1, 15):
                pks = [is_public_key(pk) for pk in o[1:]]
                if all(pks):
                    oscript = OutputScript.P2MS(o[0], len(o) - 1, o[1:])
                else:
                    raise Exception("Bad output")
            elif is_public_key(o):
                oscript = OutputScript.P2PK(o)
            elif is_btc_addr(o, network):
                oscript = OutputScript.P2PKH(o)
            elif is_script(o):
                oscript = OutputScript.P2SH(o)
            else:
                raise Exception("Bad output")

            outs.append(deepcopy(oscript))

        for i in range(len(prev_tx_id)):
            # Temporarily set IS content to 0, since data will be signed afterwards.
            iscript = InputScript()
            ins.append(iscript)

        # Once all inputs and outputs has been formatted as scripts, we could construct the transaction with the proper
        # builder.
        tx = cls.build_from_scripts(prev_tx_id, prev_out_index, value, ins,
                                    outs)

        return tx
Ejemplo n.º 7
0
from bitcoin_tools.wallet import hash_160

pk = "04a01f076082a713a82b47ace012934052bcf3359c964f1963d00def84a34e7b0345efeefe037f4b0a4e160cc40a7fac052523da88398630c07ef3c54b47aa6046"
signature = "3045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb191901"
btc_addr = "mgwpBW3g4diqasfxzWDgSi5fBrsFKmNdva"
data = ["OP_0", signature]
script = "OP_1 <" + pk + "> <" + pk + "> OP_2 OP_CHECKMULTISIG"
script_hash = b2a_hex(hash_160(Script.serialize(script)))

sigs = [signature]
pks = [pk, pk]


print ("OUTPUT SCRIPTS")

o = OutputScript.P2PK(pk)
print o.type, o.content

o = OutputScript.P2PKH(btc_addr)
print o.type, o.content

o = OutputScript.P2MS(1, 2, pks)
print o.type, o.content

o = OutputScript.P2SH(script_hash)
print o.type, o.content

print ("\nINPUT SCRIPTS")

i = InputScript().P2PK(signature)
print i.type, i.content
Ejemplo n.º 8
0
    def deserialize(cls, hex_tx):
        """ Builds a transaction object from the hexadecimal serialization format of a transaction that
        could be obtained, for example, from a blockexplorer.

        :param hex_tx: Hexadecimal serialized transaction.
        :type hex_tx: hex str
        :return: The transaction build using the provided hex serialized transaction.
        :rtype: TX
        """

        tx = cls()
        tx.hex = hex_tx

        # first 4 bytes - TX version
        tx.version = int(change_endianness(parse_element(tx, 4)), 16)

        # INPUTS
        tx.inputs = decode_varint(parse_varint(tx))

        if tx.inputs > 0:  # regular TX
            tx.isWitness = False
        else:  # witness TX
            tx.isWitness = True
            flag = parse_element(tx, 1)  # get flag and shift 2 bytes
            # get witness TX inputs count as varint
            tx.inputs = decode_varint(parse_varint(tx))

        for i in range(tx.inputs):
            # outpoint txid
            tx.prev_tx_id.append(change_endianness(parse_element(tx, 32)))
            #outpoint vout #
            tx.prev_out_index.append(
                int(change_endianness(parse_element(tx, 4)), 16))
            # ScriptSig
            tx.scriptSig_len.append(decode_varint(parse_varint(tx)))
            # asm/hex
            tx.scriptSig.append(
                InputScript.from_hex(parse_element(tx, tx.scriptSig_len[i])))
            # sequence
            tx.nSequence.append(int(parse_element(tx, 4), 16))

        # OUTPUTS
        tx.outputs = decode_varint(parse_varint(tx))

        for i in range(tx.outputs):
            tx.value.append(int(change_endianness(parse_element(tx, 8)), 16))
            # ScriptPubKey
            tx.scriptPubKey_len.append(decode_varint(parse_varint(tx)))
            tx.scriptPubKey.append(
                OutputScript.from_hex(parse_element(tx,
                                                    tx.scriptPubKey_len[i])))

        # WITNESS DATA
        if tx.isWitness:

            for tx_in in range(tx.inputs):

                tx.witness_count.append(decode_varint(parse_varint(tx)))

                for _ in range(tx.witness_count[tx_in]):
                    tx.scriptWitness_len[tx_in].append(
                        decode_varint(parse_varint(tx)))
                    tx.scriptWitness[tx_in].append(
                        parse_element(tx, tx.scriptWitness_len[tx_in][_]))

                tx.scriptWitness_len.append([])  # todo remove unnecessary list
                tx.scriptWitness.append([])  # todo remove unnecessary list
            del tx.scriptWitness_len[-1]
            del tx.scriptWitness[-1]

        # nLockTime
        tx.nLockTime = int(change_endianness(parse_element(tx, 4)), 16)

        if tx.offset != len(tx.hex):  # and not tx.isWitness:
            raise Exception(
                "There is some error in the serialized transaction passed as input. Transaction can't"
                " be built")
        else:
            tx.offset = 0

        return tx