Exemple #1
0
    def _do_multisig_script(self, sigs, message, current_script_sig, redeem_script, hash_type):
        # If the current script is empty or None, create it
        sig_script = None
        if current_script_sig is None or not str(current_script_sig):
            sig_bytes = [s["signature"].to_der() + pack_compact_int(hash_type) for s in sigs]

            sig_script = Script.build_multisig_sig(sigs=sig_bytes, redeem_script=redeem_script)
        else:
            # Need to extract all the sigs already present
            multisig_params = redeem_script.extract_multisig_redeem_info()
            sig_info = current_script_sig.extract_multisig_sig_info()

            # Do a few quick sanity checks
            if str(sig_info["redeem_script"]) != str(redeem_script):
                raise ValueError("Redeem script in signature script does not match redeem_script!")

            if len(sig_info["signatures"]) == multisig_params["n"]:
                # Already max number of signatures
                return current_script_sig

            # Go through the signatures and match them up to the public keys
            # in the redeem script
            pub_keys = []
            for pk in multisig_params["public_keys"]:
                pub_keys.append(crypto.PublicKey.from_bytes(pk))

            existing_sigs = []
            for s in sig_info["signatures"]:
                s1, h = s[:-1], s[-1]  # Last byte is hash_type
                existing_sigs.append(crypto.Signature.from_der(s1))
                if h != hash_type:
                    raise ValueError("hash_type does not match that of the existing signatures.")

            # Match them up
            existing_sig_indices = self._match_sigs_to_pub_keys(existing_sigs, pub_keys, message)
            sig_indices = {s["index"]: s["signature"] for s in sigs}

            # Make sure there are no dups
            all_indices = set(list(existing_sig_indices.keys()) + list(sig_indices.keys()))
            if len(all_indices) < len(existing_sig_indices) + len(sig_indices):
                raise ValueError("At least one signature matches an existing signature.")

            if len(all_indices) > multisig_params["n"]:
                raise ValueError("There are too many signatures.")

            all_sigs = []
            for i in sorted(all_indices):
                if i in existing_sig_indices:
                    all_sigs.append(existing_sig_indices[i])
                elif i in sig_indices:
                    all_sigs.append(sig_indices[i])

            all_sigs_bytes = [s.to_der() + pack_compact_int(hash_type) for s in all_sigs]
            sig_script = Script.build_multisig_sig(all_sigs_bytes, redeem_script)

        return sig_script
Exemple #2
0
    def __bytes__(self):
        """ Serializes the object into a byte stream.

        Returns:
            b (bytes): The serialized transaction.
        """
        return (pack_u32(self.version) +  # Version
                pack_compact_int(self.num_inputs) +  # Input count
                b''.join([bytes(i) for i in self.inputs]) +  # Inputs
                pack_compact_int(self.num_outputs) +  # Output count
                b''.join([bytes(o) for o in self.outputs]) +  # Outputs
                pack_u32(self.lock_time)  # Lock time
                )
Exemple #3
0
    def __bytes__(self):
        """ Serializes the object into a byte stream.

        Returns:
            b (bytes): The serialized transaction.
        """
        return (
            pack_u32(self.version)
            + pack_compact_int(self.num_inputs)  # Version
            + b"".join([bytes(i) for i in self.inputs])  # Input count
            + pack_compact_int(self.num_outputs)  # Inputs
            + b"".join([bytes(o) for o in self.outputs])  # Output count
            + pack_u32(self.lock_time)  # Outputs  # Lock time
        )
Exemple #4
0
    def sign_half_signed_payment(self, payment_tx, redeem_script):
        """Sign a half-signed payment transaction.

        Args:
            payment_tx (two1.lib.bitcoin.txn.Transaction): an object that
                contains a transaction from a customer, whether for a refund
                or general payment, to be signed by the merchant.

        Returns:
            signed_tx (two1.lib.bitcoin.txn.Transaction): an object that
                contains a transaction that has been signed by both the
                customer and the merchant.
        """
        # Verify that the deposit spend has only one input
        if len(payment_tx.inputs) != 1:
            raise InvalidPaymentError('Transaction should have one input.')

        # Get the public and private keys associated with this transaction
        merchant_public_key = redeem_script.merchant_public_key
        private_key = self._wallet.get_private_for_public(merchant_public_key)

        # Sign the first (and only) input in the transaction
        sig = payment_tx.get_signature_for_input(0, Transaction.SIG_HASH_ALL,
                                                 private_key, redeem_script)[0]

        # Update input script sig
        payment_tx.inputs[0].script.insert(
            1,
            sig.to_der() + utils.pack_compact_int(Transaction.SIG_HASH_ALL))

        # Return a Transaction containing the fully-signed transaction.
        return payment_tx
Exemple #5
0
    def sign_half_signed_payment(self, payment_tx, redeem_script):
        """Sign a half-signed payment transaction.

        Args:
            payment_tx (two1.lib.bitcoin.txn.Transaction): an object that
                contains a transaction from a customer, whether for a refund
                or general payment, to be signed by the merchant.

        Returns:
            signed_tx (two1.lib.bitcoin.txn.Transaction): an object that
                contains a transaction that has been signed by both the
                customer and the merchant.
        """
        # Verify that the deposit spend has only one input
        if len(payment_tx.inputs) != 1:
            raise InvalidPaymentError('Transaction should have one input.')

        # Get the public and private keys associated with this transaction
        merchant_public_key = redeem_script.merchant_public_key
        private_key = self._wallet.get_private_for_public(merchant_public_key)

        # Sign the first (and only) input in the transaction
        sig = payment_tx.get_signature_for_input(0, Transaction.SIG_HASH_ALL, private_key, redeem_script)[0]

        # Update input script sig
        payment_tx.inputs[0].script.insert(1, sig.to_der() + utils.pack_compact_int(Transaction.SIG_HASH_ALL))

        # Return a Transaction containing the fully-signed transaction.
        return payment_tx
Exemple #6
0
    def __bytes__(self):
        """ Serializes the Block object into a byte stream.

        Returns:
            b (bytes): The serialized byte stream.
        """
        return (bytes(self.block_header) + pack_compact_int(len(self.txns)) +
                b''.join([bytes(t) for t in self.txns]))
Exemple #7
0
    def sign_input(self, input_index, hash_type, private_key, sub_script):
        """ Signs an input.

        Args:
            input_index (int): The index of the input to sign.
            hash_type (int): What kind of signature hash to do.
            private_key (crypto.PrivateKey): private key with which
                to sign the transaction.
            sub_script (Script): the scriptPubKey of the corresponding
                utxo being spent if the outpoint is P2PKH or the redeem
                script if the outpoint is P2SH.
        """
        if input_index < 0 or input_index >= len(self.inputs):
            raise ValueError("Invalid input index.")

        inp = self.inputs[input_index]

        multisig = False
        if sub_script.is_multisig_redeem():
            multisig = True
        elif not sub_script.is_p2pkh():
            raise TypeError("Signing arbitrary redeem scripts is not currently supported.")

        tmp_script = sub_script.remove_op("OP_CODESEPARATOR")

        # Before signing we should verify that the address in the
        # sub_script corresponds to that of the private key
        m = self._match_public_key(private_key, tmp_script)
        if not m['match']:
            if multisig:
                msg = "Public key derived from private key does not match any of the public keys in redeem script."
            else:
                msg = "Address derived from private key does not match sub_script!"
            raise ValueError(msg)

        sig, signed_message = self.get_signature_for_input(
            input_index, hash_type, private_key, sub_script)

        if multisig:
            # For multisig, we need to determine if there are already
            # signatures and if so, where we insert this signature
            inp.script = self._do_multisig_script(
                [dict(index=m['info']['multisig_key_index'],
                      signature=sig)],
                signed_message,
                inp.script,
                tmp_script,
                hash_type)
        else:
            pub_key_bytes = self._get_public_key_bytes(private_key,
                                                       m['info']['compressed'])
            inp.script = Script([sig.to_der() + pack_compact_int(hash_type),
                                 pub_key_bytes])

        return True
Exemple #8
0
    def __bytes__(self):
        """ Serializes the Block object into a byte stream.

        Returns:
            b (bytes): The serialized byte stream.
        """
        return (
            bytes(self.block_header) +
            pack_compact_int(len(self.txns)) +
            b''.join([bytes(t) for t in self.txns])
        )
Exemple #9
0
    def sign_input(self, input_index, hash_type, private_key, sub_script):
        """ Signs an input.

        Args:
            input_index (int): The index of the input to sign.
            hash_type (int): What kind of signature hash to do.
            private_key (crypto.PrivateKey): private key with which
                to sign the transaction.
            sub_script (Script): the scriptPubKey of the corresponding
                utxo being spent if the outpoint is P2PKH or the redeem
                script if the outpoint is P2SH.
        """
        if input_index < 0 or input_index >= len(self.inputs):
            raise ValueError("Invalid input index.")

        inp = self.inputs[input_index]

        multisig = False
        if sub_script.is_multisig_redeem():
            multisig = True
        elif not sub_script.is_p2pkh():
            raise TypeError(
                "Signing arbitrary redeem scripts is not currently supported.")

        tmp_script = sub_script.remove_op("OP_CODESEPARATOR")

        # Before signing we should verify that the address in the
        # sub_script corresponds to that of the private key
        m = self._match_public_key(private_key, tmp_script)
        if not m['match']:
            if multisig:
                msg = "Public key derived from private key does not match any of the public keys in redeem script."
            else:
                msg = "Address derived from private key does not match sub_script!"
            raise ValueError(msg)

        sig, signed_message = self.get_signature_for_input(
            input_index, hash_type, private_key, sub_script)

        if multisig:
            # For multisig, we need to determine if there are already
            # signatures and if so, where we insert this signature
            inp.script = self._do_multisig_script(
                [dict(index=m['info']['multisig_key_index'], signature=sig)],
                signed_message, inp.script, tmp_script, hash_type)
        else:
            pub_key_bytes = self._get_public_key_bytes(private_key,
                                                       m['info']['compressed'])
            inp.script = Script(
                [sig.to_der() + pack_compact_int(hash_type), pub_key_bytes])

        return True
Exemple #10
0
    def _do_multisig_script(self, sigs, message, current_script_sig,
                            redeem_script, hash_type):
        # If the current script is empty or None, create it
        sig_script = None
        if current_script_sig is None or not str(current_script_sig):
            sig_bytes = [
                s['signature'].to_der() + pack_compact_int(hash_type)
                for s in sigs
            ]

            sig_script = Script.build_multisig_sig(sigs=sig_bytes,
                                                   redeem_script=redeem_script)
        else:
            # Need to extract all the sigs already present
            multisig_params = redeem_script.extract_multisig_redeem_info()
            sig_info = current_script_sig.extract_multisig_sig_info()

            # Do a few quick sanity checks
            if str(sig_info['redeem_script']) != str(redeem_script):
                raise ValueError(
                    "Redeem script in signature script does not match redeem_script!"
                )

            if len(sig_info['signatures']) == multisig_params['n']:
                # Already max number of signatures
                return current_script_sig

            # Go through the signatures and match them up to the public keys
            # in the redeem script
            pub_keys = []
            for pk in multisig_params['public_keys']:
                pub_keys.append(crypto.PublicKey.from_bytes(pk))

            existing_sigs = []
            for s in sig_info['signatures']:
                s1, h = s[:-1], s[-1]  # Last byte is hash_type
                existing_sigs.append(crypto.Signature.from_der(s1))
                if h != hash_type:
                    raise ValueError(
                        "hash_type does not match that of the existing signatures."
                    )

            # Match them up
            existing_sig_indices = self._match_sigs_to_pub_keys(
                existing_sigs, pub_keys, message)
            sig_indices = {s['index']: s['signature'] for s in sigs}

            # Make sure there are no dups
            all_indices = set(list(existing_sig_indices.keys()) + \
                              list(sig_indices.keys()))
            if len(all_indices) < len(existing_sig_indices) + len(sig_indices):
                raise ValueError(
                    "At least one signature matches an existing signature.")

            if len(all_indices) > multisig_params['n']:
                raise ValueError("There are too many signatures.")

            all_sigs = []
            for i in sorted(all_indices):
                if i in existing_sig_indices:
                    all_sigs.append(existing_sig_indices[i])
                elif i in sig_indices:
                    all_sigs.append(sig_indices[i])

            all_sigs_bytes = [
                s.to_der() + pack_compact_int(hash_type) for s in all_sigs
            ]
            sig_script = Script.build_multisig_sig(all_sigs_bytes,
                                                   redeem_script)

        return sig_script
Exemple #11
0
    def sign_input(self, input_index, hash_type, private_key, sub_script):
        """ Signs an input.

        Args:
            input_index (int): The index of the input to sign.
            hash_type (int): What kind of signature hash to do.
            private_key (crypto.PrivateKey): private key with which
                to sign the transaction.
            sub_script (Script): the scriptPubKey of the corresponding
                utxo being spent if the outpoint is P2PKH or the redeem
                script if the outpoint is P2SH.
        """
        if input_index < 0 or input_index >= len(self.inputs):
            raise ValueError("Invalid input index.")

        inp = self.inputs[input_index]

        curr_script_sig = inp.script
        multisig = False
        multisig_params = None
        multisig_key_index = -1
        if sub_script.is_multisig_redeem():
            multisig = True
            multisig_params = sub_script.extract_multisig_redeem_info()
        elif not sub_script.is_p2pkh():
            raise TypeError("Signing arbitrary redeem scripts is not currently supported.")

        tmp_script = sub_script.remove_op("OP_CODESEPARATOR")

        compressed = False
        if hash_type & 0x1f == self.SIG_HASH_SINGLE and len(self.inputs) > len(self.outputs):
            # This is to deal with the bug where specifying an index
            # that is out of range (wrt outputs) results in a
            # signature hash of 0x1 (little-endian)
            msg_to_sign = 0x1.to_bytes(32, 'little')
        else:
            txn_copy = self._copy_for_sig(input_index, hash_type, tmp_script)

            if multisig:
                # Determine which of the public keys this private key
                # corresponds to.
                public_keys = multisig_params['public_keys']
                pub_key_full = self._get_public_key_bytes(private_key, False)
                pub_key_comp = self._get_public_key_bytes(private_key, True)

                for i, p in enumerate(public_keys):
                    if pub_key_full == p or pub_key_comp == p:
                        multisig_key_index = i
                        break

                if multisig_key_index == -1:
                    raise ValueError(
                        "Public key derived from private key does not match any of the public keys in redeem script.")
            else:
                # Before signing we should verify that the address in the
                # sub_script corresponds to that of the private key
                script_pub_key_h160_hex = tmp_script.get_hash160()
                if script_pub_key_h160_hex is None:
                    raise ValueError("Couldn't find public key hash in sub_script!")

                # first try uncompressed key
                h160 = None
                for compressed in [True, False]:
                    h160 = private_key.public_key.hash160(compressed)
                    if h160 != bytes.fromhex(script_pub_key_h160_hex[2:]):
                        h160 = None
                    else:
                        break

                if h160 is None:
                    raise ValueError("Address derived from private key does not match sub_script!")

            msg_to_sign = bytes(Hash.dhash(bytes(txn_copy) +
                                           pack_u32(hash_type)))

        sig = private_key.sign(msg_to_sign, False)

        if multisig:
            # For multisig, we need to determine if there are already
            # signatures and if so, where we insert this signature
            inp.script = self._do_multisig_script([dict(index=multisig_key_index,
                                                        signature=sig)],
                                                  msg_to_sign,
                                                  curr_script_sig,
                                                  tmp_script,
                                                  hash_type)
        else:
            pub_key_bytes = self._get_public_key_bytes(private_key, compressed)
            pub_key_str = pack_var_str(pub_key_bytes)
            script_sig = pack_var_str(
                sig.to_der() + pack_compact_int(hash_type)) + pub_key_str
            inp.script = Script(script_sig)

        return True