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
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
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:])
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