def create_p2pkh_transaction(private_key, unspents, outputs, custom_pushdata=False): public_key = private_key.public_key public_key_len = len(public_key).to_bytes(1, byteorder='little') scriptCode = private_key.scriptcode scriptCode_len = int_to_varint(len(scriptCode)) version = VERSION_1 lock_time = LOCK_TIME # sequence = SEQUENCE hash_type = HASH_TYPE input_count = int_to_unknown_bytes(len(unspents), byteorder='little') output_count = int_to_unknown_bytes(len(outputs), byteorder='little') output_block = construct_output_block(outputs, custom_pushdata=custom_pushdata) # Optimize for speed, not memory, by pre-computing values. inputs = [] for unspent in unspents: script = hex_to_bytes(unspent.script) script_len = int_to_unknown_bytes(len(script), byteorder='little') txid = hex_to_bytes(unspent.txid)[::-1] txindex = unspent.txindex.to_bytes(4, byteorder='little') amount = unspent.amount.to_bytes(8, byteorder='little') inputs.append(TxIn(script, script_len, txid, txindex, amount)) hashPrevouts = double_sha256(b''.join([i.txid + i.txindex for i in inputs])) hashSequence = double_sha256(b''.join([SEQUENCE for i in inputs])) hashOutputs = double_sha256(output_block) # scriptCode_len is part of the script. for i, txin in enumerate(inputs): to_be_hashed = (version + hashPrevouts + hashSequence + txin.txid + txin.txindex + scriptCode_len + scriptCode + txin.amount + SEQUENCE + hashOutputs + lock_time + hash_type) hashed = sha256(to_be_hashed) # BIP-143: Used for Bitcoin Cash # signature = private_key.sign(hashed) + b'\x01' signature = private_key.sign(hashed) + b'\x41' script_sig = (len(signature).to_bytes(1, byteorder='little') + signature + public_key_len + public_key) inputs[i].script = script_sig inputs[i].script_len = int_to_unknown_bytes(len(script_sig), byteorder='little') return bytes_to_hex(version + input_count + construct_input_block(inputs) + output_count + output_block + lock_time)
def create_pushdata(lst_of_pushdata): """ Creates encoded OP_RETURN pushdata as bytes Returns binary encoded OP_RETURN pushdata (automatically adds intervening OP_CODES specifying number of bytes in each pushdata element) 0x6a (i.e. OP_RETURN) is added in other, auxiliary functions; only pushdata is returned here. Max 220 bytes of pushdata Parameters ---------- lst_of_pushdata : a list of tuples (pushdata, encoding) where encoding is either "hex" or "utf-8" Returns ------- pushdata : bytes Examples -------- lst_of_pushdata = [('6d01', 'hex'), ('bitPUSHER', 'utf-8')] as per memo.cash protocol @ https://memo.cash/protocol this results in a "Set name" action to "bitPUSHER" raw OP_RETURN will be: 0e 6a 02 6d01 09 626974505553484552 0e - 14 bytes to follow (in hex) 6a - OP_RETURN 02 - 2 bytes of pushdata to follow 6d01 - "communication channel" for memo.cash - "set name" action 09 - 9 bytes to follow 626974505553484552 - "bitPUSHER" utf-8 encoded bytes --> hex representation Currently (this module) only allows up to 220 bytes maximum - as multiple OP_RETURNS in one transaction is considered non-standard.""" pushdata = b'' for i in range(len(lst_of_pushdata)): encoding = lst_of_pushdata[i][1] if encoding == 'utf-8': pushdata += get_op_pushdata_code( lst_of_pushdata[i][0]) + lst_of_pushdata[i][0].encode('utf-8') elif encoding == 'hex' and len(lst_of_pushdata[i][0]) % 2 != 0: raise ValueError( "hex encoded pushdata must have length = a multiple of two") elif encoding == 'hex' and len(lst_of_pushdata[i][0]) % 2 == 0: hex_data_as_bytes = utils.hex_to_bytes(lst_of_pushdata[i][0]) pushdata += get_op_pushdata_code( hex_data_as_bytes) + hex_data_as_bytes # check for size if len(pushdata) > 220: raise ValueError( "Total bytes in OP_RETURN cannot exceed 220 bytes at present - apologies" ) return pushdata
def calc_txid(tx_hex): return bytes_to_hex(double_sha256(hex_to_bytes(tx_hex))[::-1])
def test_construct_input_block(): assert construct_input_block(INPUTS) == hex_to_bytes(INPUT_BLOCK)
def test_pushdata_message(self): BYTES = len(b'hello').to_bytes(1, byteorder='little') + b'hello' assert construct_output_block(OUTPUTS + [(BYTES, 0)], custom_pushdata=True) == hex_to_bytes( OUTPUT_BLOCK_MESSAGE_PUSHDATA)
def test_message(self): assert construct_output_block(OUTPUTS + MESSAGES) == hex_to_bytes( OUTPUT_BLOCK_MESSAGES)
def test_no_message(self): assert construct_output_block(OUTPUTS) == hex_to_bytes(OUTPUT_BLOCK)
def test_hex_to_bytes(): assert hex_to_bytes(HEX) == BYTES_BIG assert hex_to_bytes(ODD_HEX) == ODD_HEX_BYTES