def create_p2pkh_tx_multikeys(private_keys, unspents, outputs, custom_pushdata=False): if len(private_keys) != len(unspents): raise ValueError( "'private_keys' count ({}) doesn't match 'unspent' count ({}).". format(len(private_keys), len(unspents))) version = VERSION_1 lock_time = LOCK_TIME # sequence = SEQUENCE hash_type = HASH_TYPE 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 = private_keys[i] 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)) 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 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