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 test_invalid(self): assert not verify_sig(INVALID_SIGNATURE, DATA, PUBLIC_KEY_COMPRESSED)
def test_high_s(self): with pytest.raises(InvalidSignature): verify_sig(SIGNATURE_HIGH_S, DATA, PUBLIC_KEY_COMPRESSED, strict=True)
def test_sign(self): base_key = BaseKey() data = os.urandom(200) signature = base_key.sign(data) assert verify_sig(signature, data, base_key.public_key)
def test_strict_valid(self): assert verify_sig(VALID_SIGNATURE, DATA, PUBLIC_KEY_COMPRESSED, strict=True)
def test_invalid(self): with pytest.raises(InvalidSignature): verify_sig(b'invalid', DATA, PUBLIC_KEY_COMPRESSED)
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 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_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)