Example #1
0
    def __init__(self, height, raw_script, sequence=MAX_INT, block_version=3):
        self.height = height
        if block_version == 1:
            scr = raw_script
        else:
            scr = Script.build_push_int(self.height) + raw_script

        # Coinbase scripts are basically whatever, so we don't
        # try to create a script object from them.

        super().__init__(self.NULL_OUTPOINT, self.MAX_INT, scr, sequence)
Example #2
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
Example #3
0
    def _copy_for_sig(self, input_index, hash_type, sub_script):
        """ Returns a copy of this txn appropriate for signing, based
            on hash_type.
        """
        new_txn = copy.deepcopy(self)

        # First deal w/the inputs

        # For the SIG_HASH_ANY case, we only care about
        # self.inputs[input_index]
        if hash_type == self.SIG_HASH_ANY:
            ti = new_txn.inputs[input_index]
            new_txn.inputs = [ti]
        else:
            for i, inp in enumerate(new_txn.inputs):
                inp.script = sub_script if i == input_index else Script("")

                if hash_type & 0x1f in [
                        self.SIG_HASH_NONE, self.SIG_HASH_SINGLE
                ] and input_index != i:
                    # Sequence numbers (nSequence) must be set to 0 for all but
                    # the input we care about.
                    inp.sequence_num = 0

        # Now deal with outputs

        if hash_type & 0x1f == self.SIG_HASH_NONE:
            new_txn.outputs = []
        elif hash_type & 0x1f == self.SIG_HASH_SINGLE:
            # Resize output vector to input_index + 1
            new_txn.outputs = new_txn.outputs[:input_index + 1]
            # All outputs except outputs[i] have a value of -1 (0xffffffff)
            # and a blank script
            for i, out in enumerate(new_txn.outputs):
                if i != input_index:
                    out.script = Script("")
                    out.value = 0xffffffff

        return new_txn
Example #4
0
    def from_bytes(b):
        """ Deserializes a byte stream into a TransactionOutput object.

        Args:
            b (bytes): byte-stream beginning with the value.

        Returns:
            tuple:
                First element of the tuple is a TransactionOutput,
                the second is the remainder of the byte stream.
        """
        value, b0 = unpack_u64(b)
        script_len, b0 = unpack_compact_int(b0)

        return (TransactionOutput(value,
                                  Script(b0[:script_len])), b0[script_len:])
Example #5
0
    def _verify_input(self, input_index, sub_script, partial_multisig=False):
        p2sh = sub_script.is_p2sh()

        sig_script = self.inputs[input_index].script

        si = ScriptInterpreter(txn=self,
                               input_index=input_index,
                               sub_script=sub_script)
        try:
            si.run_script(sig_script)
        except ScriptInterpreterError:
            return False

        # This copy_stack and the restore_stack emulate the behavior
        # found in bitcoin core for evaluating P2SH scripts. See:
        # https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1170
        if p2sh:
            si.copy_stack()

        try:
            si.run_script(sub_script)
        except ScriptInterpreterError:
            return False
        rv = si.valid

        if p2sh:
            si.restore_stack()
            redeem_script = Script(si.stack.pop())
            si._sub_script = redeem_script

            try:
                if sig_script.is_multisig_sig() and partial_multisig:
                    # This is a hack for partial verification
                    partial_script = copy.deepcopy(redeem_script)
                    partial_script.ast[-1] = 'OP_CHECKPARTIALMULTISIG'

                    sig_info = sig_script.extract_multisig_sig_info()
                    si.run_script(partial_script)
                    rv &= si.match_count > 0 and si.match_count <= len(
                        sig_info['signatures'])
                else:
                    si.run_script(redeem_script)
                    rv &= si.valid
            except:
                rv = False

        return rv
Example #6
0
    def from_bytes(b):
        """ Deserializes a byte stream into a TransactionInput.

        Args:
            b (bytes): byte stream starting with the outpoint.

        Returns:
            tuple:
                 First element of the tuple is the TransactionInput
                 object and the second is the remaining byte stream.
        """
        outpoint = b[0:32]
        outpoint_index, b1 = unpack_u32(b[32:])
        script, b1 = Script.from_bytes(b1)
        sequence_num, b1 = unpack_u32(b1)

        return (TransactionInput(Hash(outpoint), outpoint_index, script,
                                 sequence_num), b1)
Example #7
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