def deserialize(cls, hex_tx): """ Builds a transaction object from the hexadecimal serialization format of a transaction that could be obtained, for example, from a blockexplorer. :param hex_tx: Hexadecimal serialized transaction. :type hex_tx: hex str :return: The transaction build using the provided hex serialized transaction. :rtype: TX """ tx = cls() tx.hex = hex_tx tx.version = int(change_endianness(parse_element(tx, 4)), 16) # INPUTS tx.inputs = int(parse_varint(tx), 16) for i in range(tx.inputs): tx.prev_tx_id.append(change_endianness(parse_element(tx, 32))) tx.prev_out_index.append( int(change_endianness(parse_element(tx, 4)), 16)) # ScriptSig tx.scriptSig_len.append(int(parse_varint(tx), 16)) tx.scriptSig.append( InputScript.from_hex(parse_element(tx, tx.scriptSig_len[i]))) tx.nSequence.append(int(parse_element(tx, 4), 16)) # OUTPUTS tx.outputs = int(parse_varint(tx), 16) for i in range(tx.outputs): tx.value.append(int(change_endianness(parse_element(tx, 8)), 16)) # ScriptPubKey tx.scriptPubKey_len.append(int(parse_varint(tx), 16)) tx.scriptPubKey.append( OutputScript.from_hex(parse_element(tx, tx.scriptPubKey_len[i]))) tx.nLockTime = int(parse_element(tx, 4), 16) if tx.offset != len(tx.hex): raise Exception( "There is some error in the serialized transaction passed as input. Transaction can't" " be built") else: tx.offset = 0 return tx
def display(self): """ Displays all the information related to the transaction object, properly split and arranged. Data between parenthesis corresponds to the data encoded following the serialized transaction format. (replicates the same encoding being done in serialize method) :param self: self :type self: TX :return: None. :rtype: None """ print "version: " + str(self.version) + " (" + change_endianness( int2bytes(self.version, 4)) + ")" print "number of inputs: " + str(self.inputs) + " (" + encode_varint( self.inputs) + ")" for i in range(self.inputs): print "input " + str(i) print "\t previous txid (little endian): " + self.prev_tx_id[i] + \ " (" + change_endianness(self.prev_tx_id[i]) + ")" print "\t previous tx output (little endian): " + str(self.prev_out_index[i]) + \ " (" + change_endianness(int2bytes(self.prev_out_index[i], 4)) + ")" print "\t input script (scriptSig) length: " + str(self.scriptSig_len[i]) \ + " (" + encode_varint((self.scriptSig_len[i])) + ")" print "\t input script (scriptSig): " + self.scriptSig[i].content print "\t decoded scriptSig: " + Script.deserialize( self.scriptSig[i].content) if self.scriptSig[i].type is "P2SH": print "\t \t decoded redeemScript: " + InputScript.deserialize( self.scriptSig[i].get_element(-1)[1:-1]) print "\t nSequence: " + str(self.nSequence[i]) + " (" + int2bytes( self.nSequence[i], 4) + ")" print "number of outputs: " + str(self.outputs) + " (" + encode_varint( self.outputs) + ")" for i in range(self.outputs): print "output " + str(i) print "\t Satoshis to be spent (little endian): " + str(self.value[i]) + \ " (" + change_endianness(int2bytes(self.value[i], 8)) + ")" print "\t output script (scriptPubKey) length: " + str(self.scriptPubKey_len[i]) \ + " (" + encode_varint(self.scriptPubKey_len[i]) + ")" print "\t output script (scriptPubKey): " + self.scriptPubKey[ i].content print "\t decoded scriptPubKey: " + Script.deserialize( self.scriptPubKey[i].content) print "nLockTime: " + str(self.nLockTime) + " (" + int2bytes( self.nLockTime, 4) + ")"
def signature_format(self, index, hashflag=SIGHASH_ALL, orphan=False, network='test'): """ Builds the signature format an unsigned transaction has to follow in order to be signed. Basically empties every InputScript field but the one to be signed, identified by index, that will be filled with the OutputScript from the UTXO that will be redeemed. The format of the OutputScripts will depend on the hashflag: - SIGHASH_ALL leaves OutputScript unchanged. - SIGHASH_SINGLE should sign each input with the output of the same index (not implemented yet). - SIGHASH_NONE empies all the outputs. - SIGHASH_ANYONECANPAY not sure about what should do (obviously not implemented yet). :param index: The index of the input to be signed. :type index: int :param hashflag: Hash type to be used, see above description for further information. :type hashflag: int :param orphan: Whether the input is orphan or not. Orphan inputs must provide an OutputScript that matches the utxo to be redeemed. :type orphan: OutputScript :param network: Network into which the transaction will be published (either mainnet or testnet). :type network: str :return: Transaction properly formatted to be signed. :rtype TX """ tx = deepcopy(self) for i in range(tx.inputs): if i is index: if not orphan: script, t = get_prev_ScriptPubKey(tx.prev_tx_id[i], tx.prev_out_index[i], network) # Once we get the previous UTXO script, the inputScript is temporarily set to it in order to sign # the transaction. tx.scriptSig[i] = InputScript.from_hex(script) tx.scriptSig[i].type = t else: # If input to be signed is orphan, the orphan InputScript is used when signing the transaction. tx.scriptSig[i] = orphan tx.scriptSig_len[i] = len(tx.scriptSig[i].content) / 2 elif tx.scriptSig[i].content != "": # All other scriptSig fields are emptied and their length is set to 0. tx.scriptSig[i] = InputScript() tx.scriptSig_len[i] = len(tx.scriptSig[i].content) / 2 if hashflag is SIGHASH_SINGLE: # First we checks if the input that we are trying to sign has a corresponding output, if so, the execution # can continue. Otherwise, we abort the signature process since it could lead to a irreversible lose of # funds due to a bug in SIGHASH_SINGLE. # https://bitcointalk.org/index.php?topic=260595 if index >= tx.outputs: raise Exception( "You are trying to use SIGHASH_SINGLE to sign an input that does not have a " "corresponding output (" + str(index) + "). This could lead to a irreversible lose " "of funds. Signature process aborted.") # Otherwise, all outputs will set to empty scripts but the ith one (identified by index), # since SIGHASH_SINGLE should only sign the ith input with the ith output. else: # How to properly deal with SIGHASH_SINGLE signature format extracted from: # https://github.com/bitcoin/bitcoin/blob/3192e5278a/test/functional/test_framework/script.py#L869 # First we backup the output that we will sign, t_script = tx.scriptPubKey[index] t_size = tx.scriptPubKey_len[index] t_value = tx.value[index] # Then, we delete every single output. tx.scriptPubKey = [] tx.scriptPubKey_len = [] tx.value = [] for o in range(index): # Once the all outputs have been deleted, we create empty outputs for every single index before # the one that will be signed. Furthermore, the value of the output if set to maximum (2^64-1) tx.scriptPubKey.append(OutputScript()) tx.scriptPubKey_len.append( len(tx.scriptPubKey[o].content) / 2) tx.value.append(pow(2, 64) - 1) # Once we reach the index of the output that will be signed, we restore it with the one that we backed # up before. tx.scriptPubKey.append(t_script) tx.scriptPubKey_len.append(t_size) tx.value.append(t_value) # Finally, we recalculate the number of outputs for the signature format. # Notice that each signature format will have index number of outputs! Otherwise it will be invalid. tx.outputs = len(tx.scriptPubKey) elif hashflag is SIGHASH_NONE: # Empty all the scriptPubKeys and set the length and the output counter to 0. tx.outputs = 0 tx.scriptPubKey = OutputScript() tx.scriptPubKey_len = len(tx.scriptPubKey.content) / 2 elif hashflag is SIGHASH_ANYONECANPAY: # ToDo: Implement SIGHASH_ANYONECANPAY pass if hashflag in [SIGHASH_SINGLE, SIGHASH_NONE]: # All the nSequence from inputs except for the current one (index) is set to 0. # https://github.com/bitcoin/bitcoin/blob/3192e5278a/test/functional/test_framework/script.py#L880 for i in range(tx.inputs): if i is not index: tx.nSequence[i] = 0 return tx
def sign(self, sk, index, hashflag=SIGHASH_ALL, compressed=True, orphan=False, deterministic=True, network='test'): """ Signs a transaction using the provided private key(s), index(es) and hash type. If more than one key and index is provides, key i will sign the ith input of the transaction. :param sk: Private key(s) used to sign the ith transaction input (defined by index). :type sk: SigningKey or list of SigningKey. :param index: Index(es) to be signed by the provided key(s). :type index: int or list of int :param hashflag: Hash type to be used. It will define what signature format will the unsigned transaction have. :type hashflag: int :param compressed: Indicates if the public key that goes along with the signature will be compressed or not. :type compressed: bool :param orphan: Whether the inputs to be signed are orphan or not. Orphan inputs are those who are trying to redeem from a utxo that has not been included in the blockchain or has not been seen by other nodes. Orphan inputs must provide a dict with the index of the input and an OutputScript that matches the utxo to be redeemed. e.g: orphan_input = dict({0: OutputScript.P2PKH(btc_addr)) :type orphan: dict(index, InputScript) :param deterministic: Whether the signature is performed using a deterministic k or not. Set by default. :type deterministic: bool :param network: Network from which the previous ScripPubKey will be queried (either main or test). :type network: str :return: Transaction signature. :rtype: str """ # Normalize all parameters if isinstance(sk, list) and isinstance(index, int): # In case a list for multisig is received as only input. sk = [sk] if isinstance(sk, SigningKey): sk = [sk] if isinstance(index, int): index = [index] for i in range(len(sk)): # If the input to be signed is orphan, the OutputScript of the UTXO to be redeemed will be passed to # the signature_format function, otherwise False is passed and the UTXO will be requested afterwards. o = orphan if not orphan else orphan.get(i) # The unsigned transaction is formatted depending on the input that is going to be signed. For input i, # the ScriptSig[i] will be set to the scriptPubKey of the UTXO that input i tries to redeem, while all # the other inputs will be set blank. unsigned_tx = self.signature_format(index[i], hashflag, o, network) # Then, depending on the format how the private keys have been passed to the signing function # and the content of the ScripSig field, a different final scriptSig will be created. if isinstance( sk[i], list) and unsigned_tx.scriptSig[index[i]].type is "P2MS": sigs = [] for k in sk[i]: sigs.append( ecdsa_tx_sign(unsigned_tx.serialize(), k, hashflag, deterministic)) iscript = InputScript.P2MS(sigs) elif isinstance(sk[i], SigningKey) and unsigned_tx.scriptSig[ index[i]].type is "P2PK": s = ecdsa_tx_sign(unsigned_tx.serialize(), sk[i], hashflag, deterministic) iscript = InputScript.P2PK(s) elif isinstance(sk[i], SigningKey) and unsigned_tx.scriptSig[ index[i]].type is "P2PKH": s = ecdsa_tx_sign(unsigned_tx.serialize(), sk[i], hashflag, deterministic) pk = serialize_pk(sk[i].get_verifying_key(), compressed) iscript = InputScript.P2PKH(s, pk) elif unsigned_tx.scriptSig[index[i]].type is "unknown": raise Exception( "Unknown previous transaction output script type. Can't sign the transaction." ) else: raise Exception("Can't sign input " + str(i) + " with the provided data.") # Finally, temporal scripts are stored as final and the length of the script is computed self.scriptSig[i] = iscript self.scriptSig_len[i] = len(iscript.content) / 2 self.hex = self.serialize()
def build_from_io(cls, prev_tx_id, prev_out_index, value, outputs, fees=None, network='test'): """ Builds a transaction from a collection of inputs and outputs, such as previous transactions references and output references (either public keys, Bitcoin addresses, list of public keys (for multisig transactions), etc). This builder leaves the transaction ready to sign, so its the one to be used in most cases (Standard transactions). outputs format: P2PKH -> Bitcoin address, or list of Bitcoin addresses. e.g: output = btc_addr or output = [btc_addr0, btc_addr1, ...] P2PK -> Serialized Public key, or list of serialized pubic keys. (use keys.serialize_pk) e.g: output = pk or output = [pk0, pk1, ...] P2MS -> List of int (m) and public keys, or list of lists of int (m_i) and public keys. m represent the m-of-n number of public keys needed to redeem the transaction. e.g: output = [n, pk0, pk1, ...] or output = [[n_0, pk0_0, pk0_1, ...], [n_1, pk1_0, pk1_1, ...], ...] P2SH -> script hash (hash160 str hex) or list of hash 160s. e.g: output = da1745e9b549bd0bfa1a569971c77eba30cd5a4b or output = [da1745e9b549bd0bfa1a569971c77eba30cd5a4b, ...] :param prev_tx_id: Previous transaction id. :type prev_tx_id: either str or list of str :param prev_out_index: Previous output index. Together with prev_tx_id represent the UTXOs the current transaction is aiming to redeem. :type prev_out_index: either str or list of str :param value: Value in Satoshis to be spent. :type value: either int or list of int :param outputs: Information to build the output of the transaction. :type outputs: See above outputs format. :param fees: Fees that will be applied to the transaction. If set, fees will be subtracted from the last output. :type fees: int :param network: Network into which the transaction will be published (either mainnet or testnet). :type network: str :return: Transaction build with the input and output provided data. :rtype: TX """ ins = [] outs = [] # Normalize all parameters if isinstance(prev_tx_id, str): prev_tx_id = [prev_tx_id] if isinstance(prev_out_index, int): prev_out_index = [prev_out_index] if isinstance(value, int): value = [value] if isinstance(outputs, str) or (isinstance(outputs, list) and isinstance(outputs[0], int)): outputs = [outputs] # If fees have been set, subtract them from the final value. Otherwise, assume they have been already # subtracted when specifying the amounts. if fees: value[-1] -= fees if len(prev_tx_id) != len(prev_out_index): raise Exception( "Previous transaction id and index number of elements must match. " + str(len(prev_tx_id)) + "!= " + str(len(prev_out_index))) elif len(value) != len(outputs): raise Exception( "Each output must have set a Satoshi amount. Use 0 if no value is going to be transferred." ) for o in outputs: # Multisig outputs are passes ad an integer m representing the m-of-n transaction, amb m public keys. if isinstance(o, list) and o[0] in range(1, 15): pks = [is_public_key(pk) for pk in o[1:]] if all(pks): oscript = OutputScript.P2MS(o[0], len(o) - 1, o[1:]) else: raise Exception("Bad output") elif is_public_key(o): oscript = OutputScript.P2PK(o) elif is_btc_addr(o, network): oscript = OutputScript.P2PKH(o) elif is_script(o): oscript = OutputScript.P2SH(o) else: raise Exception("Bad output") outs.append(deepcopy(oscript)) for i in range(len(prev_tx_id)): # Temporarily set IS content to 0, since data will be signed afterwards. iscript = InputScript() ins.append(iscript) # Once all inputs and outputs has been formatted as scripts, we could construct the transaction with the proper # builder. tx = cls.build_from_scripts(prev_tx_id, prev_out_index, value, ins, outs) return tx
print ("OUTPUT SCRIPTS") o = OutputScript.P2PK(pk) print o.type, o.content o = OutputScript.P2PKH(btc_addr) print o.type, o.content o = OutputScript.P2MS(1, 2, pks) print o.type, o.content o = OutputScript.P2SH(script_hash) print o.type, o.content print ("\nINPUT SCRIPTS") i = InputScript().P2PK(signature) print i.type, i.content i = InputScript().P2PKH(signature, pk) print i.type, i.content i = InputScript().P2MS(sigs) print i.type, i.content i = InputScript().P2SH(data, script) print i.type, i.content
def display(self): """ Displays all the information related to the transaction object, properly split and arranged. Data between parenthesis corresponds to the data encoded following the serialized transaction format. (replicates the same encoding being done in serialize method) :param self: self :type self: TX :return: None. :rtype: None """ print("version: " + str(self.version) + " (" + change_endianness(int2bytes(self.version, 4)) + ")") if self.version == 2: print( 'BIP68 Relative lock-time using consensus-enforced sequence numbers' ) if self.isWitness: print('Witness TX') else: print('Non-Witness TX') print("number of inputs: " + str(self.inputs) + " (" + encode_varint(self.inputs) + ")") for i in range(self.inputs): print("input " + str(i)) print("\t previous txid (little endian): " + self.prev_tx_id[i] + " (" + change_endianness(self.prev_tx_id[i]) + ")") print("\t previous tx output (little endian): " + str(self.prev_out_index[i]) + " (" + change_endianness(int2bytes(self.prev_out_index[i], 4)) + ")") print("\t input script (scriptSig) length: " + str(self.scriptSig_len[i]) + " (" + encode_varint((self.scriptSig_len[i])) + ")") print("\t input script (scriptSig): " + self.scriptSig[i].content) print("\t decoded scriptSig: " + Script.deserialize(self.scriptSig[i].content)) if self.scriptSig[i].type == "P2SH": print("\t \t decoded redeemScript: " + InputScript.deserialize( self.scriptSig[i].get_element(-1)[1:-1])) print("\t nSequence: " + str(self.nSequence[i]) + " (" + int2bytes(self.nSequence[i], 4) + ")") if self.isWitness: # todo! chack if there a TX with more than 1 witnes vin or with witness 0 print("\t txinwitness:") for _ in self.scriptWitness[i]: print('\t\t', _) print("number of outputs: " + str(self.outputs) + " (" + encode_varint(self.outputs) + ")") for i in range(self.outputs): print("output " + str(i)) print("\t Satoshis to be spent (little endian): " + str(self.value[i]) + " (" + change_endianness(int2bytes(self.value[i], 8)) + ")") print("\t output script (scriptPubKey) length: " + str(self.scriptPubKey_len[i]) + " (" + encode_varint(self.scriptPubKey_len[i]) + ")") print("\t output script (scriptPubKey): " + self.scriptPubKey[i].content) print("\t decoded scriptPubKey: " + Script.deserialize(self.scriptPubKey[i].content)) print("nLockTime: " + str(self.nLockTime) + " (" + int2bytes(self.nLockTime, 4) + ")")
def deserialize(cls, hex_tx): """ Builds a transaction object from the hexadecimal serialization format of a transaction that could be obtained, for example, from a blockexplorer. :param hex_tx: Hexadecimal serialized transaction. :type hex_tx: hex str :return: The transaction build using the provided hex serialized transaction. :rtype: TX """ tx = cls() tx.hex = hex_tx # first 4 bytes - TX version tx.version = int(change_endianness(parse_element(tx, 4)), 16) # INPUTS tx.inputs = decode_varint(parse_varint(tx)) if tx.inputs > 0: # regular TX tx.isWitness = False else: # witness TX tx.isWitness = True flag = parse_element(tx, 1) # get flag and shift 2 bytes # get witness TX inputs count as varint tx.inputs = decode_varint(parse_varint(tx)) for i in range(tx.inputs): # outpoint txid tx.prev_tx_id.append(change_endianness(parse_element(tx, 32))) #outpoint vout # tx.prev_out_index.append( int(change_endianness(parse_element(tx, 4)), 16)) # ScriptSig tx.scriptSig_len.append(decode_varint(parse_varint(tx))) # asm/hex tx.scriptSig.append( InputScript.from_hex(parse_element(tx, tx.scriptSig_len[i]))) # sequence tx.nSequence.append(int(parse_element(tx, 4), 16)) # OUTPUTS tx.outputs = decode_varint(parse_varint(tx)) for i in range(tx.outputs): tx.value.append(int(change_endianness(parse_element(tx, 8)), 16)) # ScriptPubKey tx.scriptPubKey_len.append(decode_varint(parse_varint(tx))) tx.scriptPubKey.append( OutputScript.from_hex(parse_element(tx, tx.scriptPubKey_len[i]))) # WITNESS DATA if tx.isWitness: for tx_in in range(tx.inputs): tx.witness_count.append(decode_varint(parse_varint(tx))) for _ in range(tx.witness_count[tx_in]): tx.scriptWitness_len[tx_in].append( decode_varint(parse_varint(tx))) tx.scriptWitness[tx_in].append( parse_element(tx, tx.scriptWitness_len[tx_in][_])) tx.scriptWitness_len.append([]) # todo remove unnecessary list tx.scriptWitness.append([]) # todo remove unnecessary list del tx.scriptWitness_len[-1] del tx.scriptWitness[-1] # nLockTime tx.nLockTime = int(change_endianness(parse_element(tx, 4)), 16) if tx.offset != len(tx.hex): # and not tx.isWitness: raise Exception( "There is some error in the serialized transaction passed as input. Transaction can't" " be built") else: tx.offset = 0 return tx