def create_p2pkh_transaction(utxosets, outputs, custom_pushdata=False): version = VERSION_1 lock_time = LOCK_TIME # sequence = SEQUENCE hash_type = HASH_TYPE unspents = [Unspent.from_dict(utxo) for utxo in utxosets] input_count = int_to_varint(len(unspents)) output_count = int_to_varint(len(outputs)) output_block = construct_output_block(outputs, custom_pushdata=custom_pushdata) # Optimize for speed, not memory, by pre-computing values. inputs = [] for unspent in unspents: 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('', 0, 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): private_key = bsv(wif=utxosets[i]['PrivateKey']) public_key = bytes.fromhex(private_key.public_key) public_key_len = len(public_key).to_bytes(1, byteorder='little') scriptCode = (OP_DUP + OP_HASH160 + OP_PUSH_20 + address_to_public_key_hash(private_key.address) + OP_EQUALVERIFY + OP_CHECKSIG) scriptCode_len = int_to_varint(len(scriptCode)) 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 SV # 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_varint(len(script_sig)) return bytes_to_hex(version + input_count + construct_input_block(inputs) + output_count + output_block + lock_time)
def get_rawtx_to_pay(sighash_single_rawtx, pay_to_address): est_size_of_rawHex = len(sighash_single_rawtx['version']) +\ len(sighash_single_rawtx['input']) + \ len(int_to_varint(2).hex()) + \ len(sighash_single_rawtx["output"]) + \ len(sighash_single_rawtx['lock_time']) + \ 16 + 2 + 50 # amount: 8 bytes script_len: 1 byte script: 25bytes rawtx_single_json = deserialize_input(sighash_single_rawtx['input']) input_amount = 0 for item in rawtx_single_json: result = get_tx_by_txid(item['txid']) input_amount += int(result['vout'][item['txindex']]['value'] * 100000000) output_amount = int.from_bytes( bytes.fromhex(sighash_single_rawtx['output'][:8]), 'little') amount = int(input_amount - output_amount - est_size_of_rawHex / 4) #fee rate: 0.5sat/B output_count = int_to_varint(2) output_block = bytes.fromhex(sighash_single_rawtx["output"]) output_script = (OP_DUP + OP_HASH160 + OP_PUSH_20 + address_to_public_key_hash(pay_to_address) + OP_EQUALVERIFY + OP_CHECKSIG) output_block += (amount).to_bytes(8, byteorder='little') #satoshi output_block += int_to_varint(len(output_script)) output_block += output_script rawtx = '' rawtx += sighash_single_rawtx['version'] rawtx += sighash_single_rawtx['input'] rawtx += output_count.hex() rawtx += output_block.hex() rawtx += sighash_single_rawtx['lock_time'] utxoset = {} utxoset['txid'] = calc_txid(rawtx) utxoset['txindex'] = 1 utxoset['amount'] = amount utxoset['confirmations'] = 0 return { 'rawtx': rawtx, 'utxoset': utxoset, 'txid': calc_txid(rawtx), 'amount': amount }.copy()
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_varint(n_in)) + n_out * 34 # excluding op_return outputs, dealt with separately + len(int_to_varint(n_out)) + op_return_size # grand total size of op_return outputs(s) and related field(s) + 4 # time lock ) estimated_fee = math.ceil(estimated_size * satoshis) logging.debug('Estimated fee: {} satoshis for {} bytes'.format( estimated_fee, estimated_size)) return estimated_fee
def get_op_return_size(message, custom_pushdata=False): # calculate op_return size for each individual message if custom_pushdata is False: op_return_size = ( 8 # int64_t amount 0x00000000 + len(OP_FALSE + OP_RETURN) # 2 bytes + len(get_op_pushdata_code( message)) # 1 byte if <75 bytes, 2 bytes if OP_PUSHDATA1... + len(message) # Max 220 bytes at present ) if custom_pushdata is True: op_return_size = ( 8 # int64_t amount 0x00000000 + len(OP_FALSE + OP_RETURN) # 2 bytes + len( message ) # Unsure if Max size will be >220 bytes due to extra OP_PUSHDATA codes... ) # "Var_Int" that preceeds OP_RETURN - 0xdf is max value with current 220 byte limit (so only adds 1 byte) op_return_size += len(int_to_varint(op_return_size)) return op_return_size
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_FALSE + 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_FALSE + 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" output_block += int_to_varint(len(script)) output_block += script return output_block
def generate_sighash_single_rawtx(utxosets, changeaddress, authrized_amount): unspents = [Unspent.from_dict(utxo) for utxo in utxosets] version = VERSION_1 lock_time = LOCK_TIME #sequence = SEQUENCE input_count = int_to_varint(len(unspents)) inputs = [] total_input_amount = 0 for unspent in unspents: 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('', 0, txid, txindex, amount)) total_input_amount += unspent.amount #satoshi output_count = int_to_varint(1) output_block = b'' output_script = (OP_DUP + OP_HASH160 + OP_PUSH_20 + address_to_public_key_hash(changeaddress) + OP_EQUALVERIFY + OP_CHECKSIG) output_block += (total_input_amount - authrized_amount).to_bytes( 8, byteorder='little') #satoshi output_block += int_to_varint(len(output_script)) output_block += output_script hashPrevouts = double_sha256(b''.join([i.txid + i.txindex for i in inputs])) hashSequence = bytes.fromhex( '0000000000000000000000000000000000000000000000000000000000000000') # scriptCode_len is part of the script. for i, txin in enumerate(inputs): if i == 0: hashOutputs = double_sha256(output_block) hash_type = 0x43.to_bytes(4, byteorder='little') #sighash single else: hashOutputs = bytes.fromhex( '0000000000000000000000000000000000000000000000000000000000000000' ) hash_type = 0x42.to_bytes(4, byteorder='little') #sighash none private_key = bsv(utxosets[i]['PrivateKey']) public_key = bytes.fromhex(private_key.public_key) public_key_len = len(public_key).to_bytes(1, byteorder='little') scriptCode = (OP_DUP + OP_HASH160 + OP_PUSH_20 + address_to_public_key_hash(private_key.address) + OP_EQUALVERIFY + OP_CHECKSIG) scriptCode_len = int_to_varint(len(scriptCode)) 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 SV # signature = private_key.sign(hashed) + b'\x01' sighash ALL ; single b'\x03' ,NONE b'\x02' if i == 0: signature = private_key.sign(hashed) + b'\x43' else: signature = private_key.sign(hashed) + b'\x42' 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_varint(len(script_sig)) return { "version": bytes_to_hex(version), "input": bytes_to_hex(input_count + construct_input_block(inputs)), "output": bytes_to_hex(output_block), "lock_time": bytes_to_hex(lock_time) }