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 estimate_tx_fee(n_in, n_out, satoshis, compressed, op_return_size=0): if not satoshis: return 0 estimated_size = ( 4 + # version n_in * (148 if compressed else 180) + len(int_to_unknown_bytes(n_in, byteorder='little')) + n_out * 34 # excluding op_return outputs, dealt with separately + len(int_to_unknown_bytes(n_out, byteorder='little')) + op_return_size # grand total size of op_return outputs(s) and related field(s) + 4 # time lock ) estimated_fee = estimated_size * satoshis logging.debug('Estimated fee: {} satoshis for {} bytes'.format( estimated_fee, estimated_size)) return estimated_fee
def construct_output_block(outputs, custom_pushdata=False): output_block = b'' for data in outputs: dest, amount = data # Real recipient if amount: script = (OP_DUP + OP_HASH160 + OP_PUSH_20 + address_to_public_key_hash(dest) + OP_EQUALVERIFY + OP_CHECKSIG) output_block += amount.to_bytes(8, byteorder='little') # Blockchain storage else: if custom_pushdata is False: script = OP_RETURN + get_op_pushdata_code(dest) + dest output_block += b'\x00\x00\x00\x00\x00\x00\x00\x00' elif custom_pushdata is True: # manual control over number of bytes in each batch of pushdata if type(dest) != bytes: raise TypeError("custom pushdata must be of type: bytes") else: script = (OP_RETURN + dest) output_block += b'\x00\x00\x00\x00\x00\x00\x00\x00' # Script length in wiki is "Var_int" but there's a note of "modern BitcoinQT" using a more compact "CVarInt" # CVarInt is what I believe we have here - No changes made. If incorrect - only breaks if 220 byte limit is increased. output_block += int_to_unknown_bytes(len(script), byteorder='little') output_block += script return output_block
def b58decode(string): alphabet_index = BASE58_ALPHABET_INDEX num = 0 try: for char in string: num *= 58 num += alphabet_index[char] except KeyError: raise ValueError('"{}" is an invalid base58 encoded ' 'character.'.format(char)) from None bytestr = int_to_unknown_bytes(num) pad = 0 for char in string: if char == '1': pad += 1 else: break return b'\x00' * pad + bytestr
def test_zero(self): assert int_to_unknown_bytes(0) == b'\x00'
def test_little(self): assert int_to_unknown_bytes(BIG_INT, 'little') == BYTES_LITTLE
def test_default(self): assert int_to_unknown_bytes(BIG_INT) == BYTES_BIG