def calculate_preimages(tx_obj, inputs_parameters): """Calculates preimages for provided transaction structure and input values. :param tx_obj: The transaction object used to calculate preimage from using a transaction digest algorithm, such as BIP-143 for Segwit inputs. This transaction object must hence have scriptCodes filled into the corresponding scriptSigs in the inputs. :type tx_obj: :object:`~bit.transaction.TxObj` :param inputs_parameters: A list of tuples with input index as integer, hash type as integer and a boolean flag to denote if the input is spending from a Segwit output. For example: [(0, 1, True), (2, 1, False), (...)] :type inputs_parameters: A `list` of `tuple` """ # Tx object data: input_count = int_to_varint(len(tx_obj.TxIn)) output_count = int_to_varint(len(tx_obj.TxOut)) output_block = b''.join([bytes(o) for o in tx_obj.TxOut]) hashPrevouts = double_sha256(b''.join( [i.txid + i.txindex for i in tx_obj.TxIn])) hashSequence = double_sha256(b''.join([i.sequence for i in tx_obj.TxIn])) hashOutputs = double_sha256(output_block) preimages = [] for input_index, hash_type, segwit_input in inputs_parameters: # We can only handle hashType == 1: if hash_type != HASH_TYPE: raise ValueError('Bit only support hashType of value 1.') # Calculate prehashes: if segwit_input: # BIP-143 preimage: hashed = sha256( tx_obj.version + hashPrevouts + hashSequence + tx_obj.TxIn[input_index].txid + tx_obj.TxIn[input_index].txindex + tx_obj.TxIn[input_index].script_sig_len + tx_obj.TxIn[input_index].script_sig # scriptCode length + tx_obj.TxIn[input_index]. sequence # scriptCode (includes amount) + hashOutputs + tx_obj.locktime + hash_type) else: hashed = sha256( tx_obj.version + input_count + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx_obj.TxIn, input_index)) + tx_obj.TxIn[input_index].txid + tx_obj.TxIn[input_index].txindex + tx_obj.TxIn[input_index].script_sig_len + tx_obj.TxIn[input_index].script_sig # scriptCode length + tx_obj.TxIn[input_index].sequence # scriptCode + b''.join( ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx_obj.TxIn, input_index + 1, None)) + output_count + output_block + tx_obj.locktime + hash_type) preimages.append(hashed) return preimages
def __bytes__(self): inp = int_to_varint(len(self.TxIn)) + b''.join(map(bytes, self.TxIn)) out = int_to_varint(len(self.TxOut)) + b''.join(map(bytes, self.TxOut)) wit = b''.join([w.witness for w in self.TxIn]) return b''.join([ self.version, MARKER if wit else b'', FLAG if wit else b'', inp, out, wit, self.locktime ])
def legacy_repr(self): inp = int_to_varint(len(self.TxIn)) + b''.join(map(bytes, self.TxIn)) out = int_to_varint(len(self.TxOut)) + b''.join(map(bytes, self.TxOut)) return b''.join([ self.version, inp, out, self.locktime ])
def __bytes__(self): inp = int_to_varint(len(self.TxIn)) + b''.join(map(bytes, self.TxIn)) out = int_to_varint(len(self.TxOut)) + b''.join(map(bytes, self.TxOut)) wit = b''.join([w.witness for w in self.TxIn]) return b''.join([ self.version, self.marker, self.flag, inp, out, wit, self.locktime ])
def __init__(self, script_sig, txid, txindex, witness=b'', amount=None, sequence=SEQUENCE, segwit_input=False): self.script_sig = script_sig self.script_sig_len = int_to_varint(len(script_sig)) self.txid = txid self.txindex = txindex self.witness = witness self.amount = amount self.sequence = sequence self.segwit_input = segwit_input
def deserialize(tx): if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx): return deserialize(hex_to_bytes(tx)) segwit_tx = TxObj.is_segwit(tx) version, tx = read_bytes(tx, 4) timestamp, tx = read_bytes(tx, 4) if segwit_tx: _, tx = read_bytes(tx, 1) # ``marker`` is nulled _, tx = read_bytes(tx, 1) # ``flag`` is nulled ins, tx = read_var_int(tx) inputs = [] for i in range(ins): txid, tx = read_bytes(tx, 32) txindex, tx = read_bytes(tx, 4) script_sig, tx = read_var_string(tx) sequence, tx = read_bytes(tx, 4) inputs.append(TxIn(script_sig, txid, txindex, sequence=sequence)) outs, tx = read_var_int(tx) outputs = [] for _ in range(outs): amount, tx = read_bytes(tx, 8) script_pubkey, tx = read_var_string(tx) outputs.append(TxOut(amount, script_pubkey)) if segwit_tx: for i in range(ins): wnum, tx = read_var_int(tx) witness = int_to_varint(wnum) for _ in range(wnum): w, tx = read_segwit_string(tx) witness += w inputs[i].witness = witness locktime, _ = read_bytes(tx, 4) txobj = TxObj(version, timestamp, inputs, outputs, locktime) return txobj
def __init__(self, script, txid, txindex, witness=b'', amount=0, sequence=SEQUENCE, segwit=False): self.script = script self.script_len = int_to_varint(len(script)) self.txid = txid self.txindex = txindex self.witness = witness if amount == 0 and segwit: amount = NetworkAPI.get_tx_amount( bytes_to_hex(self.txid[::-1]), int.from_bytes(self.txindex, byteorder='little')).to_bytes( 8, byteorder='little') self.amount = amount self.sequence = sequence self.segwit = segwit
def __init__(self, amount, script_pubkey): self.amount = amount self.script_pubkey = script_pubkey self.script_pubkey_len = int_to_varint(len(script_pubkey))
def __init__(self, value, script): self.value = value self.script = script self.script_len = int_to_varint(len(script))
def __init__(self, script, txid, txindex, sequence=SEQUENCE): self.script = script self.script_len = int_to_varint(len(script)) self.txid = txid self.txindex = txindex self.sequence = sequence
def sign_tx( private_key, tx, j=-1 ): # Future-TODO: add sw_dict to allow override of segwit input dictionary? # j is the input to be signed and can be a single index, a list of indices, or denote all inputs (-1) if not isinstance(tx, TxObj): # Add sw_dict containing unspent segwit txid:txindex and amount to deserialize tx: sw_dict = {} unspents = private_key.unspents for u in unspents: if u.segwit: tx_input = u.txid + ':' + str(u.txindex) sw_dict[tx_input] = u.amount tx = deserialize(tx, sw_dict, private_key.sw_scriptcode) version = tx.version marker = b'\x00' flag = b'\x01' lock_time = tx.locktime hash_type = HASH_TYPE input_count = int_to_varint(tx.input_count) output_count = int_to_varint(tx.output_count) output_block = b'' for i in range(tx.output_count): output_block += tx.TxOut[i].value output_block += tx.TxOut[i].script_len output_block += tx.TxOut[i].script hashPrevouts = double_sha256(b''.join( [i.txid + i.txindex for i in tx.TxIn])) hashSequence = double_sha256(b''.join([i.sequence for i in tx.TxIn])) hashOutputs = double_sha256(b''.join([bytes(o) for o in tx.TxOut])) if j < 0: # Sign all inputs j = range(len(tx.TxIn)) elif not isinstance(j, list): # Sign a single input j = [j] segwit = False # Global check if at least one input is segwit for i in j: # Check if input is segwit or non-segwit: sw = tx.TxIn[i].segwit segwit = segwit or sw # Global check if at least one input is segwit => Transaction must be of segwit-format public_key = private_key.public_key public_key_len = script_push(len(public_key)) scriptCode = private_key.scriptcode scriptCode_len = int_to_varint(len(scriptCode)) if sw == False: hashed = sha256(version + input_count + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx.TxIn, i)) + tx.TxIn[i].txid + tx.TxIn[i].txindex + scriptCode_len + scriptCode + tx.TxIn[i].sequence + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx.TxIn, i + 1, None)) + output_count + output_block + lock_time + hash_type) input_script_field = tx.TxIn[i].script else: hashed = sha256( # BIP-143: Used for Segwit version + hashPrevouts + hashSequence + tx.TxIn[i].txid + tx.TxIn[i].txindex + scriptCode_len + scriptCode + tx.TxIn[i].amount + tx.TxIn[i].sequence + hashOutputs + lock_time + hash_type) input_script_field = tx.TxIn[i].witness signature = private_key.sign(hashed) + b'\x01' # ------------------------------------------------------------------ if private_key.instance == 'MultiSig' or private_key.instance == 'MultiSigTestnet': # P2(W)SH input script_blob = b'' sigs = {} if input_script_field: # If tx is already partially signed: Make a dictionary of the provided signatures with public-keys as key-values sig_list = get_signatures_from_script(input_script_field) if len(sig_list) > private_key.m: raise TypeError( 'Transaction is already signed with {} of {} needed signatures.' ).format(len(sig_list), private_key.m) for sig in sig_list: for pub in private_key.public_keys: if verify_sig(sig[:-1], hashed, hex_to_bytes(pub)): sigs[pub] = sig script_blob += b'\x00' * ( private_key.m - len(sig_list) - 1 ) # Bitcoin Core convention: Every missing signature is denoted by 0x00. Only used for already partially-signed scriptSigs. sigs[bytes_to_hex(public_key)] = signature witness = b'' witness_count = 2 # count number of witness items (OP_0 + each signature + redeemscript). for pub in private_key.public_keys: # Sort the signatures according to the public-key list: if pub in sigs: sig = sigs[pub] length = int_to_varint( len(sig)) if sw == True else script_push(len(sig)) witness += length + sig witness_count += 1 script_sig = b'\x22' + private_key.sw_scriptcode witness = (witness_count.to_bytes(1, byteorder='little') if sw == True else b'') + b'\x00' + witness + script_blob witness += (int_to_varint(len( private_key.redeemscript)) if sw == True else script_push( len(private_key.redeemscript))) + private_key.redeemscript script_sig = witness if sw == False else script_sig witness = b'\x00' if sw == False else witness # ------------------------------------------------------------------ else: # P2(W)PKH input script_sig = b'\x16' + private_key.sw_scriptcode witness = ((b'\x02' if sw == True else b'') + # witness counter len(signature).to_bytes(1, byteorder='little') + signature + public_key_len + public_key) script_sig = witness if sw == False else script_sig witness = b'\x00' if sw == False else witness tx.TxIn[i].script = script_sig tx.TxIn[i].script_len = int_to_varint(len(script_sig)) tx.TxIn[i].witness = witness return bytes_to_hex( version + (marker if segwit == True else b'') + (flag if segwit == True else b'') + input_count + construct_input_block(tx.TxIn) + output_count + output_block + (construct_witness_block(tx.TxIn) if segwit == True else b'') + lock_time)
def read_segwit_string(): size = read_var_int() return int_to_varint(size) + read_bytes(size)
def deserialize(txhex, sw_dict={}, sw_scriptcode=None): # sw_dict is a dictionary containing segwit-inputs' txid concatenated with txindex using ":" mapping to information of the amount the input contains. # E.g.: sw_dict = {'txid:txindex': amount, ...} if isinstance(txhex, str) and re.match('^[0-9a-fA-F]*$', txhex): return deserialize(hex_to_bytes(txhex), sw_dict, sw_scriptcode) if txhex[ 4: 6] == b'\x00\x01': # ``marker|flag'' == 0001 if segwit-transaction segwit = True else: segwit = False pos = [0] def read_as_int(bytez): pos[0] += bytez return int(bytes_to_hex(txhex[pos[0] - bytez:pos[0]][::-1]), base=16) def read_var_int(): pos[0] += 1 val = int(bytes_to_hex(txhex[pos[0] - 1:pos[0]]), base=16) if val < 253: return val return read_as_int(pow(2, val - 252)) def read_bytes(bytez): pos[0] += bytez return txhex[pos[0] - bytez:pos[0]] def read_var_string(): size = read_var_int() return read_bytes(size) def read_segwit_string(): size = read_var_int() return int_to_varint(size) + read_bytes(size) version = read_as_int(4).to_bytes(4, byteorder='little') if segwit: _ = read_as_int(1).to_bytes(1, byteorder='little') # ``marker`` is read _ = read_as_int(1).to_bytes(1, byteorder='little') # ``flag`` is read ins = read_var_int() inputs = [] for i in range(ins): txid = read_bytes(32) txindex = read_as_int(4).to_bytes(4, byteorder='little') script = read_var_string() sequence = read_as_int(4).to_bytes(4, byteorder='little') # Check if input is segwit: tx_input = bytes_to_hex(txid) + ':' + bytes_to_hex(txindex) sw = True if ( sw_scriptcode == script[1:] or tx_input in sw_dict ) else False # Partially-signed segwit-multisig input or input provided in sw_dict. amount = sw_dict[ tx_input] if tx_input in sw_dict else 0 # Read ``amount`` from sw_dict if it is provided. inputs.append(TxIn(script, txid, txindex, b'', amount, sequence, sw)) outs = read_var_int() outputs = [] for _ in range(outs): value = read_as_int(8).to_bytes(8, byteorder='little') script = read_var_string() outputs.append(TxOut(value, script)) if segwit: for i in range(ins): wnum = read_var_int() witness = int_to_varint(wnum) for _ in range(wnum): witness += read_segwit_string() inputs[i].witness = witness locktime = read_as_int(4).to_bytes(4, byteorder='little') txobj = TxObj(version, inputs, outputs, locktime) return txobj
def __bytes__(self): inp = int_to_varint(len(self.TxIn)) + b''.join(map(bytes, self.TxIn)) out = int_to_varint(len(self.TxOut)) + b''.join(map(bytes, self.TxOut)) wit = b''.join(map(lambda w: w.witness, self.TxIn)) return b''.join([self.version, inp, out, wit, self.locktime])
def sign_tx(private_key, tx, *, unspents): """Signs inputs in provided transaction object for which unspents are provided and can be signed by the private key. :param private_key: Private key :type private_key: ``PrivateKey`` or ``MultiSig`` :param tx: Transaction object :type tx: ``TxObj`` :param unspents: For inputs to be signed their corresponding Unspent objects must be provided. :returns: The signed transaction as hex. :rtype: ``str`` """ # input_dict contains those unspents that can be signed by private_key, # providing additional information for segwit-inputs (the amount to spend) input_dict = {} try: for unspent in unspents: if not private_key.can_sign_unspent(unspent): continue tx_input = hex_to_bytes(unspent.txid)[::-1] + \ unspent.txindex.to_bytes(4, byteorder='little') input_dict[tx_input] = unspent.to_dict() except TypeError: raise ValueError('Please provide as unspents at least all inputs to ' 'be signed with the function call.') # Determine input indices to sign from input_dict (allows for transaction batching) sign_inputs = [j for j, i in enumerate(tx.TxIn) if i.txid+i.txindex in input_dict] segwit_tx = TxObj.is_segwit(tx) version = tx.version lock_time = tx.locktime hash_type = HASH_TYPE input_count = int_to_varint(len(tx.TxIn)) output_count = int_to_varint(len(tx.TxOut)) output_block = b''.join([bytes(o) for o in tx.TxOut]) hashPrevouts = double_sha256(b''.join([i.txid+i.txindex for i in tx.TxIn])) hashSequence = double_sha256(b''.join([i.sequence for i in tx.TxIn])) hashOutputs = double_sha256(output_block) for i in sign_inputs: tx_input = tx.TxIn[i].txid + tx.TxIn[i].txindex segwit_input = input_dict[tx_input]['segwit'] tx.TxIn[i].segwit_input = segwit_input public_key = private_key.public_key public_key_push = script_push(len(public_key)) script_code = private_key.scriptcode script_code_len = int_to_varint(len(script_code)) if not segwit_input: hashed = sha256( version + input_count + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx.TxIn, i)) + tx.TxIn[i].txid + tx.TxIn[i].txindex + script_code_len + script_code + tx.TxIn[i].sequence + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(tx.TxIn, i + 1, None)) + output_count + output_block + lock_time + hash_type ) input_script_field = tx.TxIn[i].script_sig elif segwit_input: try: tx.TxIn[i].amount = input_dict[tx_input]['amount']\ .to_bytes(8, byteorder='little') except Attributerror: raise ValueError( 'Cannot sign a segwit input when the input\'s amount is ' 'unknown. Maybe no network connection or the input is ' 'already spent? Then please provide all inputs to sign as ' '`Unspent` objects to the function call.') hashed = sha256( # BIP-143: Used for Segwit version + hashPrevouts + hashSequence + tx.TxIn[i].txid + tx.TxIn[i].txindex + script_code_len + script_code + tx.TxIn[i].amount + tx.TxIn[i].sequence + hashOutputs + lock_time + hash_type ) input_script_field = tx.TxIn[i].witness signature = private_key.sign(hashed) + b'\x01' # ------------------------------------------------------------------ if (private_key.instance == 'MultiSig' or private_key.instance == 'MultiSigTestnet'): # P2(W)SH input script_blob = b'' sigs = {} # Initial number of witness items (OP_0 + one signature + redeemscript). witness_count = 3 if input_script_field: sig_list = get_signatures_from_script(input_script_field) # Bitcoin Core convention: Every missing signature is denoted # by 0x00. Only used for already partially-signed scriptSigs: script_blob += b'\x00' * (private_key.m - len(sig_list)-1) # Total number of witness items when partially or fully signed: witness_count = private_key.m + 2 # For a partially signed input make a dictionary containing # all the provided signatures with public-keys as keys: for sig in sig_list: for pub in private_key.public_keys: if verify_sig(sig[:-1], hashed, hex_to_bytes(pub)): # If we already found a valid signature for pubkey # we just overwrite it and don't care. sigs[pub] = sig if len(sigs) == private_key.m: raise TypeError('Transaction is already signed with ' 'sufficiently needed signatures.') elif len(sigs) > private_key.m: raise TypeError('Transaction already contains {} ' 'signatures, but only {} needed.').format( len(sigs), private_key.m) sigs[bytes_to_hex(public_key)] = signature witness = b'' # Sort ingthe signatures according to the public-key list: for pub in private_key.public_keys: if pub in sigs: sig = sigs[pub] length = int_to_varint(len(sig)) if segwit_input else \ script_push(len(sig)) witness += length + sig script_sig = b'\x22' + private_key.segwit_scriptcode witness = (int_to_varint(witness_count) if segwit_input else b'') \ + b'\x00' + witness + script_blob witness += (int_to_varint(len(private_key.redeemscript)) if segwit_input else script_push(len(private_key.redeemscript))) \ + private_key.redeemscript script_sig = script_sig if segwit_input else witness witness = witness if segwit_input else b'\x00' if segwit_tx else b'' # ------------------------------------------------------------------ else: # P2(W)PKH input script_sig = b'\x16' + private_key.segwit_scriptcode witness = ( (b'\x02' if segwit_input else b'') + # witness counter len(signature).to_bytes(1, byteorder='little') + signature + public_key_push + public_key ) script_sig = script_sig if segwit_input else witness witness = witness if segwit_input else b'\x00' if segwit_tx else b'' # Providing the signature(s) to the input tx.TxIn[i].script_sig = script_sig tx.TxIn[i].script_sig_len = int_to_varint(len(script_sig)) tx.TxIn[i].witness = witness return tx.to_hex()
def sign_tx(private_key, tx, *, unspents): """Signs inputs in provided transaction object for which unspents are provided and can be signed by the private key. :param private_key: Private key :type private_key: ``PrivateKey`` or ``MultiSig`` :param tx: Transaction object :type tx: ``TxObj`` :param unspents: For inputs to be signed their corresponding Unspent objects must be provided. :type unspents: ``list`` of :class:`~bit.network.meta.Unspent` :returns: The signed transaction as hex. :rtype: ``str`` """ # input_dict contains those unspents that can be signed by private_key, # providing additional information for segwit-inputs (the amount to spend) input_dict = {} try: for unspent in unspents: if not private_key.can_sign_unspent(unspent): continue tx_input = hex_to_bytes( unspent.txid)[::-1] + unspent.txindex.to_bytes( 4, byteorder='little') input_dict[tx_input] = unspent.to_dict() except TypeError: raise TypeError( 'Please provide as unspents at least all inputs to be signed with the function call in a list.' ) # Determine input indices to sign from input_dict (allows for transaction batching) sign_inputs = [ j for j, i in enumerate(tx.TxIn) if i.txid + i.txindex in input_dict ] segwit_tx = TxObj.is_segwit(tx) public_key = private_key.public_key public_key_push = script_push(len(public_key)) hash_type = HASH_TYPE # Make input parameters for preimage calculation inputs_parameters = [] # The TxObj in `tx` will below be modified to contain the scriptCodes used # for the transaction structure to be signed # `input_script_field` copies the scriptSigs for partially signed # transactions to later extract signatures from it: input_script_field = [tx.TxIn[i].script_sig for i in range(len(tx.TxIn))] for i in sign_inputs: # Create transaction object for preimage calculation tx_input = tx.TxIn[i].txid + tx.TxIn[i].txindex segwit_input = input_dict[tx_input]['segwit'] tx.TxIn[i].segwit_input = segwit_input script_code = private_key.scriptcode script_code_len = int_to_varint(len(script_code)) # Use scriptCode for preimage calculation of transaction object: tx.TxIn[i].script_sig = script_code tx.TxIn[i].script_sig_len = script_code_len if segwit_input: try: tx.TxIn[i].script_sig += input_dict[tx_input][ 'amount'].to_bytes(8, byteorder='little') # For partially signed Segwit transactions the signatures must # be extracted from the witnessScript field: input_script_field[i] = tx.TxIn[i].witness except AttributeError: raise ValueError( 'Cannot sign a segwit input when the input\'s amount is ' 'unknown. Maybe no network connection or the input is ' 'already spent? Then please provide all inputs to sign as ' '`Unspent` objects to the function call.') inputs_parameters.append([i, hash_type, segwit_input]) preimages = calculate_preimages(tx, inputs_parameters) # Calculate signature scripts: for hash, (i, _, segwit_input) in zip(preimages, inputs_parameters): signature = private_key.sign(hash) + b'\x01' # ------------------------------------------------------------------ if private_key.instance == 'MultiSig' or private_key.instance == 'MultiSigTestnet': # P2(W)SH input script_blob = b'' sigs = {} # Initial number of witness items (OP_0 + one signature + redeemscript). witness_count = 3 if input_script_field[i]: sig_list = get_signatures_from_script(input_script_field[i]) # Bitcoin Core convention: Every missing signature is denoted # by 0x00. Only used for already partially-signed scriptSigs: script_blob += b'\x00' * (private_key.m - len(sig_list) - 1) # Total number of witness items when partially or fully signed: witness_count = private_key.m + 2 # For a partially signed input make a dictionary containing # all the provided signatures with public-keys as keys: for sig in sig_list: for pub in private_key.public_keys: if verify_sig(sig[:-1], hash, pub): # If we already found a valid signature for pubkey # we just overwrite it and don't care. sigs[pub] = sig if len(sigs) >= private_key.m: raise ValueError( 'Transaction is already signed with sufficiently needed signatures.' ) sigs[public_key] = signature witness = b'' # Sort ingthe signatures according to the public-key list: for pub in private_key.public_keys: if pub in sigs: sig = sigs[pub] length = int_to_varint( len(sig)) if segwit_input else script_push(len(sig)) witness += length + sig script_sig = b'\x22' + private_key.segwit_scriptcode witness = (int_to_varint(witness_count) if segwit_input else b'') + b'\x00' + witness + script_blob witness += (int_to_varint(len( private_key.redeemscript)) if segwit_input else script_push( len(private_key.redeemscript))) + private_key.redeemscript script_sig = script_sig if segwit_input else witness witness = witness if segwit_input else b'\x00' if segwit_tx else b'' # ------------------------------------------------------------------ else: # P2(W)PKH input script_sig = b'\x16' + private_key.segwit_scriptcode witness = ((b'\x02' if segwit_input else b'') + len(signature).to_bytes( 1, byteorder='little') # witness counter + signature + public_key_push + public_key) script_sig = script_sig if segwit_input else witness witness = witness if segwit_input else b'\x00' if segwit_tx else b'' # Providing the signature(s) to the input tx.TxIn[i].script_sig = script_sig tx.TxIn[i].script_sig_len = int_to_varint(len(script_sig)) tx.TxIn[i].witness = witness return tx.to_hex()
def sign_legacy_tx(private_key, tx, j=-1): # j is the input to be signed and can be a single index, a list of indices, or denote all inputs (-1) if not isinstance(tx, TxObj): tx = deserialize(tx) version = tx.version lock_time = tx.locktime hash_type = HASH_TYPE input_count = int_to_varint(tx.input_count) output_count = int_to_varint(tx.output_count) output_block = b'' for i in range(tx.output_count): output_block += tx.TxOut[i].value output_block += tx.TxOut[i].script_len output_block += tx.TxOut[i].script inputs = tx.TxIn if j < 0: j = range(len(inputs)) elif not isinstance(j, list): j = [j] for i in j: public_key = private_key.public_key public_key_len = script_push(len(public_key)) scriptCode = private_key.scriptcode scriptCode_len = int_to_varint(len(scriptCode)) hashed = sha256(version + input_count + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(inputs, i)) + inputs[i].txid + inputs[i].txindex + scriptCode_len + scriptCode + inputs[i].sequence + b''.join(ti.txid + ti.txindex + OP_0 + ti.sequence for ti in islice(inputs, i + 1, None)) + output_count + output_block + lock_time + hash_type) signature = private_key.sign(hashed) + b'\x01' # ------------------------------------------------------------------ if private_key.instance == 'MultiSig' or private_key.instance == 'MultiSigTestnet': script_blob = b'' sigs = {} if tx.TxIn[ i].script: # If tx is already partially signed: Make a dictionary of the provided signatures with public-keys as key-values sig_list = get_signatures_from_script(tx.TxIn[i].script) if len(sig_list) > private_key.m: raise TypeError( 'Transaction is already signed with {} of {} needed signatures.' ).format(len(sig_list), private_key.m) for sig in sig_list: for pub in private_key.public_keys: if verify_sig(sig[:-1], hashed, hex_to_bytes(pub)): sigs[pub] = sig script_blob += b'\x00' * ( private_key.m - len(sig_list) - 1 ) # Bitcoin Core convention: Every missing signature is denoted by 0x00. Only used for already partially-signed scriptSigs. sigs[bytes_to_hex(public_key)] = signature script_sig = b'' # P2SH - Multisig for pub in private_key.public_keys: # Sort the signatures according to the public-key list: if pub in sigs: sig = sigs[pub] length = script_push(len(sig)) script_sig += length + sig script_sig = b'\x00' + script_sig + script_blob script_sig += script_push(len( private_key.redeemscript)) + private_key.redeemscript # ------------------------------------------------------------------ else: script_sig = ( # P2PKH len(signature).to_bytes(1, byteorder='little') + signature + public_key_len + public_key) inputs[i].script = script_sig inputs[i].script_len = int_to_varint(len(script_sig)) return bytes_to_hex(version + input_count + construct_input_block(inputs) + output_count + output_block + lock_time)